POJ 2752 Seek the Name, Seek the Fame
Description
The little cat is so famous, that many couples tramp over hill and dale to Byteland, and asked the little cat to give names to their newly-born babies. They seek the name, and at the same time seek the fame. In order to escape from such boring job, the innovative little cat works out an easy but fantastic algorithm:
Step1. Connect the father’s name and the mother’s name, to a new string S.
Step2. Find a proper prefix-suffix string of S (which is not only the prefix, but also the suffix of S).
Example: Father=’ala’, Mother=’la’, we have S = ‘ala’+’la’ = ‘alala’. Potential prefix-suffix strings of S are {‘a’, ‘ala’, ‘alala’}. Given the string S, could you help the little cat to write a program to calculate the length of possible prefix-suffix strings of S? (He might thank you by giving your baby a name:)
Input
The input contains a number of test cases. Each test case occupies a single line that contains the string S described above.
Restrictions: Only lowercase letters may appear in the input. 1 <= Length of S <= 400000.
Output
For each test case, output a single line with integer numbers in increasing order, denoting the possible length of the new baby’s name.
Sample Input
ababcababababcabab
aaaaa
Sample Output
2 4 9 18
1 2 3 4 5
Source
POJ Monthly–2006.01.22,Zeyuan Zhu
Translate:
本题想要求字符串s中如果有一个子串x既是前缀又是后缀则输出其长度(按升序输出)。
样例解释:
NO1.
ababcababababcabab
ab______________ab 2
abab__________abab 4
ababcabab··ababcabab 9
NO2.
aaaaa
当然1~5个a都满足
Solution:
最初的想法很简单,首先要明确next[i]数组的具体含义,(很重要!)
next[i]表示 前i位中 ,前缀 、后缀 中 最大公共子串的长度
所以可以知道next[n],也就是最长的公共子串长度,然后就可以枚举长度了。
然后用两个字符串数组tmp1,tmp2,分别表示前缀和后缀,然后一比较就好了,不过最初的想法比较朴素,又分别用两个函数每次重新实现,不过对于前缀来说每次加一位就好了,但是后缀就不怎么好弄了。经高人指导,用了strncmp就可以很简单的实现构造后缀
但是。。后缀数组会TLE(谁知道为什么)
#include<stdio.h>
#include<string.h>
#include<deque>
#define MAXN 1000000+1000
using namespace std;
int ilen;
char s[MAXN],tmp1[MAXN],tmp2[MAXN];
int next[MAXN];
void get_next()
{
next[0]=-1;
ilen=strlen(s);
int i=0,j=-1;
while(i!=ilen)
{
if(s[i]==s[j]||j==-1)
{
next[++i]=++j;
}
else
j=next[j];
}
}
int main()
{
while(~scanf("%s",s))
{
memset(next,0,sizeof(next));
get_next();
int max_length=next[ilen],k=0;
for(int i=1;i<=max_length;i++)
{
tmp1[i-1]=s[i-1];
//printf("%s\n%s\n",tmp1,tmp2);
if(strncmp(tmp1,s+max_length-i,i)==0)
{
printf("%d ",i);
}
}
printf("%d\n",ilen);
}
return 0;
}
正解比较巧妙。。已知next[n],如果有一个串满足题意,那么这个子串的最后一位一定是s的最后一位,所以我们可以一点点缩小范围,不断递归,ans就是前t位的子串长度,即t+1;
Code:
#include<stdio.h>
#include<string.h>
#include<deque>
#define MAXN 1000000+1000
using namespace std;
int ilen;
char s[MAXN],tmp1[MAXN],tmp2[MAXN];
int next[MAXN],ans[MAXN];
void get_next()
{
next[0]=-1;
ilen=strlen(s);
int i=0,j=-1;
while(i!=ilen)
{
if(s[i]==s[j]||j==-1)
{
next[++i]=++j;
}
else
j=next[j];
}
}
int main()
{
while(~scanf("%s",s))
{
//memset(next,0,sizeof(next));
get_next();
int t=next[ilen-1],cnt=0;
while(t!=-1)
{
if(s[t]==s[ilen-1])
{
ans[++cnt]=t+1;
}
t=next[t];
}
for(int i=cnt;i>=1;i--)
{
printf("%d ",ans[i]);
}
printf("%d\n",ilen);
}
return 0;
}
看看应该可以看懂。
小结
KMP是一个很神奇的东西,它将strstr函数的O(n*m)缩短为了O(n+m)。而这个改变就可以完成“一年到一秒的”飞跃,毕竟我们不是大数据,所以算法的时间空间复杂度还是很重要的,而KMP的精髓及时求next[]数组。网上有很多模拟过程都非常好,好好利用会非常有用(如求循环节,查找另一字符串在s中出现的次数等等)
人多力量大(K_ M_ P)