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> ...
随机推荐
- AVR单片机教程——烧写hex文件
每一次build项目,编译器都会生成多个文件,其中有一个就是hex文件.之前在IDE中配置的external tools,就是把这个hex文件烧写到单片机中去的. 然而,有些时候你想运行别人的程序,但 ...
- Spring AOP日志实现(一)
前置通知:获取访问的类,访问的方法,带参数和不带参数的 日志表信息描述字段: 获取访问时长:
- nfs挂载文件
1. 安装必备插件 以防centos7默认没有启动nfs服务 yum -y install nfs-utils rpcbind # 启动 rpcbind 和配置开机自启动 systemctl sta ...
- Eclipse RCP难点:给Command传递参数(Object)
这个问题网络上没有答案,国外网站上也没有,本人研究了一天,终于搞明白如何实现,实际上是Eclipse RCP的ICommandService本身就已经提供的方法,只是网络上教的都是使用IHandler ...
- jmeter中生成UUID作为唯一标识符
在测试过程中,我们有时候需要一个唯一不重复的值(比如order_id).我之前一直用的时间戳+计数器/随机函数拼接,但是有时候效果不太好,今天知道了UUID这玩意,可以来操作下.jmeter也提供了U ...
- dotnet core2.2 通过虚拟机发布到CentOS上
自从.net core出现的时候,就知道c#的代码居然能后运行到Linux上面,以前都没想过居然这么牛逼,所以很早就想学习怎样部署上去,直到现在.net core都出现2.2了,才花时间去接触,说实话 ...
- C#ModBus Tcp的学习及Master的实现
Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式. 所以这也是我们工控领域软件开发的所必懂的通讯协议,我也是初次学习,先贴上我的学习笔记 一 ...
- VBA Exit Do语句
当想要根据特定标准退出Do循环时,可使用Exit Do语句. 它可以同时用于Do...While和Do...Until直到循环. 当Exit Do被执行时,控制器在Do循环之后立即跳转到下一个语句. ...
- Jerry Wang从2017年到2019年的自由泳学习笔记
打腿 把注意力调整到脚部,尽量不要让他打出水面,因为在空气中大腿完全是无用功,但是如果只是脚跟出水一点,倒也没什么关心,但是主观上,要控制一下,如果你听到你的打腿是"咚咚咚咚"这样 ...
- windows系统Android-sdk的下载与环境变量配置
最近一段时间在做app的开发,作为前端而言,开发app并不像android工程师那样熟悉android开发:无论是使用cordova.js或者react-native开发都需要配置android开发环 ...