传送门:poj
题意
给定一个长度为n的字符串S,求它每个前缀的最短循环节。
题解
运用kmp判循环串:
对于位置
i
i
i,若
i
%
(
i
−
n
x
t
i
)
=
0
i\%(i-nxt_i)=0
i%(i−nxti)=0,则存在循环节,循环节长度为
i
i
−
n
x
t
i
\dfrac{i}{ i-nxt_i}
i−nxtii
证明:
如图:
设字符串
S
1
,
S
2
,
S
3
,
S
4
,
S
5
,
S
6
S_1,S_2,S_3,S_4,S_5,S_6
S1,S2,S3,S4,S5,S6
由kmp性质显然
S
1
=
S
4
,
S
2
=
S
5
,
S
3
=
S
6
S_1=S_4,S_2=S_5,S_3=S_6
S1=S4,S2=S5,S3=S6
同时
S
2
=
S
4
,
S
3
=
S
5
S_2=S_4,S_3=S_5
S2=S4,S3=S5
所以
S
1
S_1
S1~
S
6
S_6
S6全部相等,且
S
i
S_i
Si为前缀
i
i
i的最短循环节。(若有更短的循环节,
n
x
t
i
nxt_i
nxti必然更大)
代码
#include<cstdio>
using namespace std;
const int N=1e6+10;
int n,nxt[N],kmp,TK;
char s[N];
int main(){
int i;
for(;;){
scanf("%d",&n);
if(!n) break;
scanf("%s",s+1);
kmp=0;
if(TK) puts("");
printf("Test case #%d\n",++TK);
for(i=2;i<=n;++i){
for(;kmp && s[kmp+1]!=s[i];kmp=nxt[kmp]);
kmp+=(s[kmp+1]==s[i]);
nxt[i]=kmp;
if(nxt[i]>0 && (i%(i-nxt[i]))==0)
printf("%d %d\n",i,i/(i-nxt[i]));
}
}
return 0;
}