题意:
给定一个字符串,现在要求在字符串末尾补上最少的单词数,使得该字符串成为带有循环节的字符串。输出最少需要加的单词数。(循环节:例如:ababab,ab为循环节,共k=3次循环,题意要求是k>=2)
题解:
我们又要用到KMP算法中的失配函数f[i],其表示的意思就是f[i]位置之前的字符串,即f[i]长的字符串前缀与i位置之前f[i]长的字符串相等。我们将字符串每i-f[i]个字符分一组,分成m组,每组记为ci,那么由于前面的性质,我们发现ci=c(i+1)。所以只有最后的剩余部分n%(n-f[n])是不在这个分组内,但由于性质这部分字符串必定是ci的前缀,所以只要补齐(n-f[n])-n%(n-f[n])个就能使得字符串带循环节了。
特殊情况,f[n]=0的时候,说明完全没有匹配的地方,需要在加n个字符,其实也可以用上述公式得到;另一种是n%(n-f[n])==0,则原本就是带循环节的,不需要在添加字符了。
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
using namespace std;
const int maxn=1e5+10;
char a[maxn];
int f[maxn];
void getFail(char *a,int *f,int n)
{
int i,j;
f[0]=f[1]=0;
for(i=1;i<n;i++)
{
j=f[i];
while(j&&a[i]!=a[j])j=f[j];
f[i+1]=a[i]==a[j]?j+1:0;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int i,j,k,n;
scanf("%s",a);
n=strlen(a);
getFail(a,f,n);
k=n-f[n];
if(f[n]==0)printf("%d\n",n);
else if(n%k==0)printf("0\n");
else printf("%d\n",k-n%k);
}
return 0;
}