POJ 2752 Seek the Name, Seek the Fame(KMP)

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)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章