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> ...
随机推荐
- PAT(B) 1057 数零壹(Java)字符串
题目链接:1057 数零壹 (20 point(s)) 题目描述 给定一串长度不超过 105 的字符串,本题要求你将其中所有英文字母的序号(字母 a-z 对应序号 1-26,不分大小写)相加,得 ...
- ubuntu下使用eclipse调试jni无法获取环境变量,本地库(java.library.path,LD_LIBRARY_PATH)等问题的解决。
首先要把本地库全部配置到LD_LIBRARY_PATH中. 然后一定要采用命令行方式启动eclipse(也可以写一个启动shell,通过桌面启动器打开这个shell),这样环境变量才会有效. 打开终端 ...
- hdu 5432
Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, so he ...
- 为什么要使用Optional
为什么使用Java Optional Why use Optional? NullPointerException 有个很有名的说法: Null Pointer References: The Bil ...
- 怎么将visual studio项目打包生成dll文件
1.打开电脑再打开visual studio软件,在软件里面新建一个项目,文件---->新建---->项目,打开新建项目窗口. 2.选择C#类工程,并为项目命名. 3.将类库文件class ...
- 基于【 建造者模式】一 || 网关zuul过滤器封装
一.springcloud的zuul网关拦截 1.黑名单拦截 2.参数验签 3.Api接口权限验证 二.网关拦截实现方式 1.继承ZuulFilter方法,实现业务逻辑 @Component @Slf ...
- 网络基础 InetAddress
IP地址是IP使用的32位(IPv4)或者128位(IPv6)位无符号数字,它是传输层协议TCP,UDP的基础.InetAddress是Java对IP地址的封装,在java.net中有许多类都使用到了 ...
- MySQL处理达到百万级数据时,如何优化?
1.两种查询引擎查询速度(myIsam 引擎 ) InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行. ...
- TLS1.3 PPT 整理
1.握手协议的目的是什么 建立共享秘钥(通常使用公钥加密).协商算法和模型以及加密使用的参数,验证身份. 2.记录协议 传输独立的信息,在堆成加密算法下保护数据传输 3.RSA Handshake S ...
- Linux sudo(CVE-2019-14287)漏洞复现过程
简述: 该漏洞编号是CVE-2019-14287. sudo是Linux系统管理指令,允许用户在不需要切换环境的前提下用其他用户的权限运行程序或命令,通常是以root身份运行命令,以减少root用户的 ...