定理:假设S的长度为len,则S存在最小循环节,循环节的长度L为len-next[len],子串为S[0…len-next[len]-1]。
(1)如果len可以被len - next[len]整除,则表明字符串S可以完全由循环节循环组成,循环周期T=len/L。
(2)如果不能,说明还需要再添加几个字母才能补全。需要补的个数是循环个数L-len%L=L-(len-L)%L=L-next[len]%L,L=len-next[len]。
例题:
hdu 1358 Period
题意:求字符串的前缀是否为周期串,若是,打印循环节的长度及循环次数;
思路:直接从i=2遍历到n,看是否符合
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char c[N];
int Next[N];
void init(int n)
{
int i=0,j=-1;
while(i<n)
{
if(j==-1||c[i]==c[j])
{
i++;
j++;
Next[i]=j;
}
else
{
j=Next[j];
}
}
}
int main()
{
int n;
int k=1;
while(~scanf("%d",&n)&&n)
{
scanf("%s",c);
memset(Next,0,sizeof(Next));
Next[0]=-1;
init(n);
printf("Test case #%d\n",k++);
for(int i=2;i<=n;i++)
{
if(i%(i-Next[i])==0&&i/(i-Next[i])>1)
{
printf("%d %d\n",i,i/(i-Next[i]));
}
}
printf("\n");
}
return 0;
}
poj 2406 Power Strings
题意:求周期子串
思路:如果len%(len-Next[len])==0,说明是周期子串,直接相除,否则输出1.
代码:
#include<stdio.h>
#include<string.h>
using namespace std;
const int N=1e6+10;
char c[N];
int Next[N];
void init(int n)
{
int i=0,j=-1;
while(i<n)
{
if(j==-1||c[i]==c[j])
{
i++;
j++;
Next[i]=j;
}
else
{
j=Next[j];
}
}
}
int main()
{
while(~scanf("%s",c))
{
if(c[0]=='.')break;
memset(Next,0,sizeof(Next));
Next[0]=-1;
int len=strlen(c);
init(len);
if(len%(len-Next[len])==0)
printf("%d\n",len/(len-Next[len]));
else printf("1\n");
}
return 0;
}