1.思路
对于一个数413413,可以生成多少个数呢?不难注意到,因为它有两个循环节,所以生成的数每个都重复了两遍,循环节个数可以通过kmp快速求出,最后求出的答案都除以一个循环节个数即可,在此不再赘述,见我的另一篇博客:http://k-xzy.cf/?p=1165
剩下的内容就是求这个数重复两遍得到的新数(比如63就是得到6363)的每一个后缀与原数的公共前缀,这样只要分别比较公共前缀的下一位就可以了。这可以用扩展kmp在线性时间内做到。
2.扩展kmp
我们求字符串A的每一个后缀A[i...lena]与B的公共前缀长度,假设这个长度为ex[i]。
首先我们要求出一个next数组,表示B的每一个后缀B[i...lenb]与B的公共前缀长度,假设此时我们求出了所有的next和ex[0]到ex[i-1],正在求ex[i]。
设p为当前已经匹配的A数组的最远地方,k为使得其到达最远点的i,即p=max(ex[k]+k-1);(k<i)
假设i+next[i-k]<=p
举个例子,字符A是DDBACBC...,字符串B是BACBT...当前i=5,容易目测得p=6,k=2.那么i-k代表的是“BAC”这一段,这一段是已知的,现在我们在字符串B中的第二个B处看一看是否它的自身匹配还在p的范围内,如果在,那么这个自身匹配还是已知的,已经扩展了的,那么很容易得到ex[i]=next[i-k]。(有点晦涩难懂?多看几遍意会一下,再联系一下kmp的内容)
假设i+next[i-k]>p,那么我们要暴力继续扩展来求ex[i]了,A字符串中从p+1开始,B字符串中从p-i+1开始,当然如果p-i+1<0,就从0开始。
那么容易目测得复杂度是线性的。
3.代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
int T;
char s1[100005],s2[200005];
int ex[200005],ne[200005],nx[200005];
int ans1,ans2,ans3;
void getne(char *B){
int a=0,i,j,p,l,le=strlen(B);
ne[0]=le;
while(a<le-1&&B[a]==B[a+1])a++;
ne[1]=a;a=1;
for(i=2;i<le;i++){
l=ne[i-a];p=a+ne[a]-1;
if(i+l>p){
j=p-i+1;if(j<0)j=0;
while(j+i<le&&B[j+i]==B[j])j++;
ne[i]=j;a=i;
}
else ne[i]=l;
}
}
void getex(char *A,char *B){
int a=0,i,j,p,l,mil,la=strlen(A),lb=strlen(B);
getne(B);
mil=min(la,lb);
while(a<mil&&A[a]==B[a])a++;
ex[0]=a;a=0;//注意a要清0
for(i=1;i<la;i++){
l=ne[i-a];p=a+ex[a]-1;
if(i+l>p){
j=p-i+1;if(j<0)j=0;
while(j<lb&&j+i<la&&A[i+j]==B[j])j++;
ex[i]=j;a=i;
}
else ex[i]=l;
}
}
void kmp(char *B){
int i,j,le=strlen(B);
nx[0]=0;
for(i=1;i<le;i++){
j=nx[i];
while(1){
if(B[i]==B[j]){nx[i+1]=j+1;break;}
if(!j){nx[i+1]=0;break;}
j=nx[j];
}
}
}
int main()
{
int i,j,la,lb,cnt=0,mod;
scanf("%d",&T);
while(T--){
cnt++;scanf("%s",s1);
strcpy(s2,s1);strcat(s2,s1);
getex(s2,s1);kmp(s1);
lb=strlen(s1);
mod=lb-nx[lb];
if(lb%mod==0)mod=lb/mod;
else mod=1;
ans1=ans2=ans3=0;
for(i=0;i<lb;i++){
if(ex[i]>=lb)ans2++;
else if(s1[ex[i]]<s2[i+ex[i]])ans3++;
else ans1++;
}
printf("Case %d: %d %d %d\n",cnt,ans1/mod,ans2/mod,ans3/mod);
}
return 0;
}