题意:给你一个长度为n的字符串,问你一共有多少Xi——从0开始到Xi的这段长度这个字符子串是循环串,并输出最多的循环节的次数;
解题思路:用kmp的next数组,我们从next数组的值中可以看出这个字串是否为循环串,例如:
void get_next() { int j,k; j=0;k=-1;next1[0]=-1; while(j<tlen) { if(k==-1||t[j]==t[k]) next1[++j]=++k; else k=next1[k]; } }
ababa
next【0】=-1,next【1】=0,next【2】=0,next【3】=1,next【4】=2;next【5】=3;
从next数组中可以看出,如果一个字串是循环的,那么对应的next【i+1】(这里的next数组是除去本身的最大前后缀,所以退后一位)*2一定是>=当前字串长度的;不然,就没有循环节覆盖整个字符子串,但这只是前提,从例子中next【5】=3也满足,但长度为5的字串并不是循环节,所以再加入条件:
if(next1[i]*2>=i) { if(i%(i-next1[i])==0) { ans[++cot][1]=i; ans[cot][2]=i/(i-next1[i]); } }
这个条件是判定这个字串是否正好被循环节完全覆盖;这样在记录一下满足的位置就行了;
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<math.h> #include<cstdlib> using namespace std; char t[1005000]; int next1[1005000]; int tlen; int ans[1005000][3]; void get_next() { int j,k; j=0;k=-1;next1[0]=-1; while(j<tlen) { if(k==-1||t[j]==t[k]) next1[++j]=++k; else k=next1[k]; } } int main() { int tt=0; while(scanf("%d",&tlen)!=EOF) { if(tlen==0) return 0; tt++; scanf("%s",t); get_next(); int cot=0; for(int i=0;i<=tlen;i++) { if(next1[i]*2>=i) { if(i%(i-next1[i])==0) { ans[++cot][1]=i; ans[cot][2]=i/(i-next1[i]); } } } printf("Test case #%d ",tt); for(int i=1;i<=cot;i++) printf("%d %d ",ans[i][1],ans[i][2]);cout<<endl; } }