poj2406
题目大意:
给出一个字符串s,求其最短的子串a,使得s=a^n(即n个a相连),输出n。s的长度小于10^6。
Sample Input
abcd aaaa ababab .
Sample Output
1 4 3
本题可以用kmp算法来做。
先算出next数组。有些KMP算法的代码中next[i]代表的是s[i+1]匹配不上时下一个位置,有些代码代表的是s[i]匹配不上时,本人用后者,即next数组要求到'\0'那位。
len表示s的长度(不包括'\0'),那么如果确实有长度非len的解的话,该子串的最小长度一定为(len-next[len])。
以上不证明了(因为不会证),参考kmp算法的原理脑补一下:p
但长度为(len-next[len])的子串不一定真的是解。比如:abcab,len-next[len]=3,长度为3的串abc或者cab显然不是解
所以需要判断len%(len-next[len])==0是否成立。
够了吗? 那么是否有必要扫描一遍s,判断是否s中存在的确实是len/(len-next[len])个该子串呢?
够了。
如下图,表示len==12,next[len]==8时的情景,len-next[len]==4,可以发现s即为3个长度为4的子串构成的
参考程序:
#include <stdio.h>
#include <stdlib.h>
char s[1000010];
int next[1000010];
int kmp_next(char s[])
{
int k=-1,j=0;
next[j]=k;
while (s[j])
{
if (k==-1 || s[k]==s[j])
{
k++;
j++;
if (s[k]==s[j])
next[j]=next[k];
else
next[j]=k;
}
else k=next[k];
}
return j;
}
int main()
{
int len,i,ans,flag,j;
scanf("%s",s);
while (s[0]!='.')
{
len=kmp_next(s);
ans=(len-next[len]);
if (len%ans!=0)
ans=len;
printf("%d\n",len/ans);
scanf("%s",s);
}
return 0;
}
POJ1961
题目大意:
给出一个字符串的长度和字符串s,对于其所有的前缀,设其长度为i,求最大的k(k>=2),使a^k=substring(s,0,i),若存在这样的k,输出i,k
输出所有满足的i,k对
Sample Input
3 aaa 12 aabaabaabaab 0
Sample Output
Test case #1 2 2 3 3 Test case #2 2 2 6 2 9 3 12 4
跟上题差不多,不过这回需要对next数组进行遍历。
同时注意求next数组的时候
if (s[k]==s[j])
next[j]=next[k];
else
next[j]=k;
如果有该优化的话,该优化得去掉,改成:
next[j]=k;
原因见kmp算法原理
参考程序:
#include <stdio.h>
#include <stdlib.h>
char s[1000010];
int next[1000010];
void kmp_next(char s[])
{
int k=-1,j=0;
next[j]=k;
while (s[j])
{
if (k==-1 || s[k]==s[j])
{
k++;
j++;
next[j]=k;
}
else k=next[k];
}
}
int main()
{
int tt,len,i,l,j=0;
scanf("%d",&len);
gets(s);
while (len!=0)
{
gets(s);
kmp_next(s);
printf("Test case #%d\n",++j);
for (i=2;i<=len;i++)
{
l=i-next[i];
if (i%l==0 && i/l>1)
printf("%d %d\n",i,i/l);
}
printf("\n");
scanf("%d",&len);
gets(s);
}
return 0;
}
需要做更基础的kmp题的可以去做poj3461,poj2752