KMP解决字符串最小循环节相关问题
经典问题 : 给出一个由某个循环节构成的字符串,要你找出最小的循环节,例如 abababab 最小循环节当是 ab ,而类似 abab 也可以成为它的循环节,但并非最短。
分析 :
对于上述问题有两个结论
如果对于next数组中的 i, 符合 i % ( i - next[i] ) == 0 && next[i] != 0 , 则说明字符串循环,而且
循环节长度为: i - next[i]
循环次数为: i / ( i - next[i] )
水平有限,用自己的语言描述怕有差错,给出一个参考博客 ==> http://www.cnblogs.com/jackge/archive/2013/01/05/2846006.html
再抛一个问题 : 有没有想过对于一个不完整的循环串要补充多少个才能使得其完整?
答案是==>(循环节长度) - len%(循环节长度) 即 (len - next[len]) - len%(len - next[len])
为什么? (以下胡扯,看不懂就掠过吧.........)
首先考虑整串就是循环节构成的情况,类似 abcxabcx 观察构造出来的next值显然满足上式,得出答案 0
那现在考虑不完整的情况,例如 abcabca 、其 next 值为 -1 0 0 0 1 2 3 4 。现在考虑末尾的 a,若没有它,而是将 c 作为末尾则会在 len 失配的时候会回溯道下一个循环节的末尾即 abca , 那现在多了一个a,那么回溯当然也应该是(循环节长度 + 1) 即 abcab,故 len 那里无论是否刚好为循环节的末尾,只是个"残"的末尾,未圆满的循环节,len-next[len]也是循环节长度,那需要补多少个呢?现在就很显然了!下面相关题目的 ① 就是这样的一个问题。
相关题目 :
题意 : 给出一个字符串,问你最少补充多少个字母才能使得字符串由两个或者以上的循环节构成
分析 : 由结论可知,如果字符串循环,那么最小循环节的长度为 len - next[len] ,并且这个字符串总长能被循环节长度整除说明字符串已经循环,否则 len % (len - next[len]) 则为多出来的部分,例如 abcabcab ==> len - next[len] = 3,而 len % 3 == 2 很明显就是余出来两个,这两个应当是循环节的头两个字母,对于其他串也可以自己模拟看看,所以需要补充的就是 循环节长度 - 多余出来的长度
- #include<stdio.h>
- #include<string.h>
- using namespace std;
- ;
- char mo[maxn];
- int Next[maxn], moL, nCase;
- inline void GetNext()
- {
- , j = -;
- Next[i] = j;
- while(i < moL){
- && mo[i]!=mo[j]) j = Next[j];
- Next[++i] = ++j;
- }
- }
- int ans()
- {
- GetNext();
- ) return moL;
- int Period_len = moL - Next[moL];
- int Remain = moL % Period_len;
- ) ;
- return Period_len - Remain;
- }
- int main(void)
- {
- scanf("%d", &nCase);
- while(nCase--){
- scanf("%s", mo);
- moL = strlen(mo);
- printf("%d\n", ans());
- }
- ;
- }
题意 : 给出一个字符串,叫你给出这个字符串存在的不同循环节长度以及个数 ( 循环节构成的不一定是整个字符串,也有可能是其子串 )
分析 : 根据以上的结论,我们只要让构造出字符串的next数组,而后一个for循环判断当前长度和当前最小循环节长度是否是倍数关系,即 i % ( i - next[i] ) == 0 && next[i] != 0,就能判断是否为一个循环节了,循环节的长度自然是 i / (i-next[i])
- #include<stdio.h>
- using namespace std;
- ;
- char mo[maxn];
- int Next[maxn], moL;
- inline void GetNext()
- {
- , j = -;
- Next[i] = j;
- while(i < moL){
- && mo[j]!=mo[i] ) j = Next[j];
- Next[++i] = ++j;
- }
- }
- inline void PrintAns()
- {
- GetNext();
- int Period;
- ; i<=moL; i++){
- ){
- Period = i - Next[i];
- ){
- printf("%d %d\n", i, i/Period);
- }
- }
- }puts("");
- }
- int main(void)
- {
- ;
- while(~scanf("%d", &moL) && moL){
- scanf("%s", mo);
- printf("Test case #%d\n", Case++);
- PrintAns();
- }
- ;
- }
③ HUST 1010 The Minimum Length
题意 : 假设 A 是一个循环字符串,现在截取 A 的某一段子串 B 出来,给出 B 问你构成 A 的循环节的最小长度是多少?
- #include<stdio.h>
- #include<string.h>
- #include<iostream>
- using namespace std;
- ;
- int Next[maxn], moL;
- char mo[maxn];
- inline void GetNext()
- {
- , j = -;
- Next[i] = j;
- while(i < moL){
- && mo[i]!=mo[j]) j = Next[j];
- Next[++i] = ++j;
- }
- }
- int Ans()
- {
- GetNext();
- ) return moL;
- else return moL - Next[moL];
- }
- int main(void)
- {
- while(~scanf("%s", mo)){
- moL = strlen(mo);
- printf("%d\n", Ans());
- }
- ;
- }
题意 : 给你一个字符串,问你它由多少个相同的字符串拼接而成
分析 : 直接找算出最小循环节长度,如果字符循环,则答案为 len / (循环节长度) ,而对于 len % (循环节长度) != 0 和 next[len] == 0 的情况答案就是 1 了
- #include<string.h>
- #include<stdio.h>
- using namespace std;
- ;
- int Next[maxn], moL;
- char mo[maxn];
- inline void GetNext()
- {
- , j = -;
- Next[i] = j;
- while(i < moL){
- && mo[i]!=mo[j]) j = Next[j];
- Next[++i] = ++j;
- }
- }
- int Ans()
- {
- GetNext();
- ) ;
- int Period = moL - Next[moL];
- ) ;
- return moL / Period;
- }
- int main(void)
- {
- ]!='.'){
- moL = strlen(mo);
- printf("%d\n", Ans());
- }
- ;
- }
⑤ POJ 2752 Seek the Name, Seek the Fame
题意 : 给出一个字符串,问你所有关于这个字符串的前缀和后缀相同的长度,比如 abcab 有 1 "a"、2 "ab"、5 "abcab"
分析 : 这里就要巧妙利用到 next 数组的性质了,根据next数组定义可以知道 next[len] 表示一个从头开始长度为 next[len] 的前缀和相同长度的后缀相等,那么next[ next[len] ]呢?next[ next[ next[len] ] ]呢?这里的一层层嵌套实际上都是一个长度为 next[ next[len] ] 或者 长度 next[ next[ next[len] ] ]的前缀和后缀相等,自己构造个数组画画图也能得出来这个规律,那么到此,这个问题是不是被圆满的解决了呢!
- #include<string.h>
- #include<stack>
- #include<stdio.h>
- using namespace std;
- ;
- char mo[maxn];
- int Next[maxn], moL;
- inline void GetNext()
- {
- , j = -;
- Next[i] = j;
- while(i < moL){
- && mo[j]!=mo[i]) j = Next[j];
- Next[++i] = ++j;
- }
- }
- inline void PrintAns()
- {
- moL = strlen(mo);
- GetNext();
- int tmp = Next[moL];
- stack<int> ans;///根据题目要求需要递增输出长度,而我们得出的答案顺序正好相反,所以利用栈存储
- ){///直到头为止
- ans.push(tmp);
- tmp = Next[tmp];
- }
- while(!ans.empty()){
- int Top = ans.top(); ans.pop();
- if(Top) printf("%d ", Top);
- }
- printf("%d\n", moL);
- }
- int main(void)
- {
- while(~scanf("%s", mo)){ PrintAns(); }
- ;
- }
KMP解决字符串最小循环节相关问题的更多相关文章
- hdu 4333"Revolving Digits"(KMP求字符串最小循环节+拓展KMP)
传送门 题意: 此题意很好理解,便不在此赘述: 题解: 解题思路:KMP求字符串最小循环节+拓展KMP ①首先,根据KMP求字符串最小循环节的算法求出字符串s的最小循环节的长度,记为 k: ②根据拓展 ...
- poj 2406 Power Strings【字符串+最小循环节的个数】
Po ...
- [POJ2406&POJ1961]用KMP解决字符串的循环问题两例
翻阅了一下网上资料,发现大部分都说这题是找规律...或是说YY出的一个算法..不会证明... 然后就脑补了一下证明 ~ 结论:对于一个字符串S[1..N],如果N mod (N-next[N])=0 ...
- KMP解决最小循环节问题
# 10035. 「一本通 2.1 练习 1」Power Strings [题目描述] 给定若干个长度 $\le 10^6$ 的字符串,询问每个字符串最多是由多少个相同的子字符串重复连接而成的.如 ...
- [KMP求最小循环节][HDU3746][Cyclic Nacklace]
题意 给你个字符串,问在字符串末尾还要添加几个字符,使得字符串循环2次以上. 解法 无论这个串是不是循环串 i-next[i] 都能求出它的最小循环节 代码: /* 思路:kmp+字符串的最小循环节问 ...
- KMP + 求最小循环节 --- POJ 2406 Power Strings
Power Strings Problem's Link: http://poj.org/problem?id=2406 Mean: 给你一个字符串,让你求这个字符串最多能够被表示成最小循环节重复多少 ...
- HDU 3746 (KMP求最小循环节) Cyclic Nacklace
题意: 给出一个字符串,要求在后面添加最少的字符是的新串是循环的,且至少有两个循环节.输出最少需要添加字符的个数. 分析: 假设所给字符串为p[0...l-1],其长度为l 有这样一个结论: 这个串的 ...
- [KMP求最小循环节][HDU1358][Period]
题意 求所有循环次数大于1的前缀 的最大循环次数和前缀位置 解法 直接用KMP求最小循环节 当满足i%(i-next[i])&&next[i]!=0 前缀循环次数大于1 最小循环节是i ...
- HDU 1358 Period(KMP+最小循环节)题解
思路: 这里只要注意一点,就是失配值和前后缀匹配值的区别,不懂的可以看看这里,这题因为对子串也要判定,所以用前后缀匹配值,其他的按照最小循环节做 代码: #include<iostream> ...
随机推荐
- C语言处理字符串
1. strtok 函数原型: char * strtok(char *str, const char * delim); 注意点: 两个入参必须为字符串数组: 第一次调用要传str, delim,后 ...
- 【Linux】一步一步学Linux——Linux发展史(01)
目录 00. 目录 01. Linux概述 02. Linux简史 03. Linux主要特性 04. Linux之父 05. Linux相关术语 06. Linux其它 07. Linux应用领域 ...
- on duplicate key mysql插入更新
insert into `test` (`job_id`, `user_name`, `total_time`) values ('12345', 'zhangsan', '10') on dupli ...
- k8s安装ingress
1. 环境准备 安装nginx-ingress-controller和backend cd /etc/yum.repos.d/mainfests 下载镜像的脚本 vi ingressnginx.sh ...
- ifconfig介绍
[root@controller01 ~]# ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNK ...
- vs2015工程转化为vs2010
转换的步骤如下: (1)将工程是.sln用记事本打开后,更换以下信息如下: Microsoft Visual Studio Solution File, Format Version 11.00 ...
- Nginx的启动、停止等命令
Windows下Nginx的启动.停止等命令 在Windows下使用Nginx,我们需要掌握一些基本的操作命令,比如:启动.停止Nginx服务,重新载入Nginx等,下面我就进行一些简单的介绍.1.启 ...
- mysql把A表数据插入到B表数据的几种方法
web开发中,我们经常需要将一个表的数据插入到另外一个表,有时还需要指定导入字段,设置只需要导入目标表中不存在的记录,虽然这些都可以在程序中拆分成简单sql来实现,但是用一个sql的话,会节省大量代码 ...
- charles 手机抓包设置
本文参考:charles 抓包手机 charles经常会进行手机上的网页抓包,比如去copy别人网站图片或脚本的时候o(∩_∩)o : 手机抓包的原理,和PC类似,手机依靠charles与服务端进行对 ...
- iptables详解(2)表中规则管理(增删改查)
我们定义了四张表:raw表.mangle表.nat表.filter表,不同的表有不同的功能 filter表用来过滤,允许哪些ip.端口访问,禁止哪些ip.端口访问,表中会有很多链 ①禁止ip地址访问我 ...