KMP POJ 2752 Seek the Name, Seek the Fame

来源:互联网 发布:知敬畏守底线心得体会 编辑:程序博客网 时间:2024/05/17 09:28

Seek the Name, Seek the Fame
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 15562 Accepted: 7875
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

暴力求解,TLE:

#include<stdio.h>#include<stdlib.h> #include<algorithm>#include<iostream>#define MAX 400005 using namespace std;char in[MAX];int ans[MAX],len,num,flag; int main(){    freopen("i.txt","r",stdin);     while(cin>>in){        len=strlen(in);        num=0;               for(int i=1;i<=len;i++){            flag=1;            for(int j=1;j<=i;j++){                if(in[len-j]!=in[i-j]){                    flag=0;                    break;                }            }            if(flag) ans[num++]=i;         }         for(int i=0;i<num;i++)  cout<<ans[i]<<" ";        cout<<endl;    }     return 0;}

利用KMP的getnext()函数,得到优化的程序:

//3172K 469MS#include<stdio.h>#include<stdlib.h> #include<algorithm>#include<iostream>#define MAX 400005 using namespace std;char s[MAX];int next[MAX],ans[MAX],len,num=0;void get_next(char *s,int*next){    memset(next,0,sizeof(next));    next[0]=-1;    len=strlen(s);    int suf=0,pre=-1;    while(suf!-len) {        if(pre==-1||s[suf]==s[pre]) next[++suf]=++pre;        else pre=next[pre];    }}int main(){    freopen("i.txt","r",stdin);    while(~scanf("%s",s)){        get_next(s,next);          ans[0]=len;        while(next[len]>0){          ans[++num]=next[len];          len=next[len];        }        for(int i=num;i>0;i--)             printf("%d ",ans[i]);        printf("%d\n",ans[0]);    }}

要理解这样做的正确性,重点是理解next数组的含义:next[i]表示模式串前i个字符最大
相同前缀和后缀(不包括字符串本身)的长度。
从next数组最后一个开始往前寻找每个相同的后缀和前缀。
next数组:
这里写图片描述
假设next[len]=k,表示模式串前k个组成的前缀和最后k个字符组成后缀的是相同的。
那么下一个相同的后缀和前缀,最后一个字符一定相同,因为后缀的最后一个字符一定不会变,假设字符串最后一个字符用红色表示,那么所有的前缀的最后一个都应该是红色的,如图所示。因此,可以通过len=next[len]=k来寻找下一个相同的前缀和后缀。

这里写图片描述


模式串的前k个和后k个相同,前k个的前m个和后m个相同,等价代换得到模式串的前m个和后m个相同,于是又得到了一个相同的前缀和后缀。


欢迎留言,积极讨论,一起进步!

0 0
原创粉丝点击