题目链接:
POJ : http://poj.org/problem?id=1961
HDU : http://acm.hdu.edu.cn/showproblem.php?pid=1358
ZOJ : http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2177
题目大意:
给定一个长度为n的字符串s,求它的每个前缀的最短循环节。换句话说,对于每个i(2<=i<=n),求一个最大的整数K>1(如果K存在),使得S的前i个字符组成的前缀是某个字符串重复K次得到的。输出所有存在K的i和对应的K。
比如对于字符串aabaabaabaab, 只有当i=2,6,9,12时K存在,且分别为2,2,3,4
分析与总结:
很经典的一道题目, 各大OJ都有收录。
开始做这题的时候,刚刚看懂KMP,没看最短循环节的相关资料,自己乱搞,花了两节离散数学课想,结果终于AC了 = =, 但不幸的是,结果发现只是再HDU和ZOJ过了,而poj的还是WA 。。。
然后学习《算法入门经典-训练指南》上面的方法,在poj上才成功AC。
虽然没能在poj上成功AC,但是经过这题的思考,对KMP的失配函数获取next的过程和原理有了更深的理解和体会。
代码:
1. 自己乱搞的代码, poj无法AC,求破
#include<cstdio> #include<cstring> const int MAXN = 1000005; char T[MAXN]; int f[MAXN]; int n; void getFail(char *P, int *f){ f[0]=f[1]=0; int start=1; int n=strlen(P); bool flag=true; for(int i=1; i<n; ++i){ int j=f[i]; while(j && P[i]!=P[j]){ flag=false; j=f[j]; } if(P[i]==P[j]){ f[i+1]=j+1; if(f[i+1]==1){ flag=true; start=i; } if(flag && (i+1)>=start*2 && (i+1)%start==0){ printf("%d %d\n",i+1,(i+1)/start); } } else{ flag=true; start=i+1; f[i+1]=0; } } } int main(){ int cas=1; char ch; while(~scanf("%d%*c",&n) && n){ printf("Test case #%d\n",cas++); gets(T); getFail(T,f); puts(""); } return 0; }
2. 从刘汝佳的大白书(算法入门经典-训练指南)学来的方法。
#include<cstdio> #include<cstring> const int MAXN = 1000005; char T[MAXN]; int f[MAXN]; int n; void getFail(char *P, int *f){ f[0]=f[1]=0; int start=1; int n=strlen(P); for(int i=1; i<n; ++i){ int j=f[i]; while(j && P[i]!=P[j]) j=f[j]; f[i+1] = P[i]==P[j]?j+1:0; } } int main(){ int cas=1; char ch; while(~scanf("%d%*c",&n) && n){ printf("Test case #%d\n",cas++); gets(T); getFail(T,f); for(int i=2; i<=n; ++i){ if(f[i]>0 && i%(i-f[i])==0){ printf("%d %d\n",i,i/(i-f[i])); } } puts(""); } return 0; }
—— 生命的意义,在于赋予它意义士。
原创 http://blog.csdn.net/shuangde800 , By D_Double (转载请标明)