[1,2...,m] 模板的前缀Pi 的循环节为 clc = i-next[i]
i % clc == 0 && i != clc 则表示前缀中有 k = i / clc 个循环
完全是参考代码模板:
代码如下:
#include<iostream> #include<stdio.h> #include<string> #include<string.h> #define N 1000005 using namespace std; char P[N]; int m; int next[N]; void Prefix_Func() { int i,k; k=0; next[1]=0; for(i=2;i<=m;i++) { while(k>0 && P[k+1]!= P[i]) k=next[k]; if(P[k+1] == P[i]) k++; next[i]=k; } } int main() { int t=1,clc; while(scanf("%d",&m) && m) { scanf("%s",P+1); Prefix_Func(); cout<<"Test case #"<<t++<<endl; for(int i=2;i<=m;i++) { clc=i-next[i]; if(i%clc == 0 && i!=clc) cout<<i<<" "<<i/clc<<endl; } cout<<endl; } return 0 ; }
poj 2406
求字符串有几个周期
kmp next函数 kmp的周期问题,深入了解kmp中next的原理
-----------------------
-----------------------
k m x j i
k=0.
由上,next【i】=j,两段红色的字符串相等(两个字符串完全相等),s[k....j]==s[m....i]
设s[x...j]=s[j....i](xj=ji)
则可得,以下简写字符串表达方式
kj=kx+xj;
mi=mj+ji;
因为xj=ji,所以kx=mj,如下图所示
-------------
-------------
k m x j
看到了没,此时又重复上面的模型了,kx=mj,所以可以一直这样递推下去
所以可以推出一个重要的性质len-next[i]为此字符串的最小循环节(i为字符串的结尾),另外如果len%(len-next[i])==0,此字符串的最小周期就为len/(len-next[i]);否则,最小周期为1,。
代码如下:
const int Max_N = 1000005; // 求后缀数组, 求循环节时只能模式下标从1开始, int next[Max_N]; char P[Max_N]; int m; void prefix(){ int i, k=0; next[1]=0; for(i =2 ; i<= m ; i++){ while(k>0 && P[k+1] != P[i]) k=next[k]; if(P[k+1] == P[i]) k++; next[i] = k; } } int main(){ while(scanf("%s",P+1) ) { if(P[1] == '.') break; m= strlen(P+1); prefix(); if(m % (m - next[m]) ==0) printf("%d ", m / (m - next[m]) ); else printf("1 "); } }
另一份代码为:
int next[1000005]; int kmp_next(char *pat) { int i=0,j=-1; next[0]=-1; int len = strlen(pat); while(i<len) { if(j==-1||pat[i]==pat[j]) next[++i]=++j; else j=next[j]; } i=len - j; if(len%i==0) return len / i; else return 1; } int main() { char s[1000005]; while(scanf("%s",s)!=EOF) { if(s[0]=='.') break; printf("%d ",kmp_next(s)); } return 0; }