1. 程式人生 > >poj2406—KMP next陣列的性質求最小週期

poj2406—KMP next陣列的性質求最小週期

題目大意:給出一個字串,求它最多有幾個連續子串構成

分析:KMP求最小週期——結論:如果一個字串有最小週期,那麼最小週期為n-next[n]


考慮整個串,根據next陣列的定義,前後匹配並且字首和相等的最長的字尾之間沒有交叉,那麼相等的部分的長度為next[n],並且從左往右相等。

如果希望中間的也是有s[1..next[n]]的幾個迴圈組成,那麼整個串就以next[n]為最小週期,但是如果這樣,next[n]就會變大,與現在的情況矛盾。

那麼n % (n-next[n])!=0,整個串為一個迴圈節,只有一個週期

①如果字首和字尾是有重疊的,如圖,那麼藍色和綠色相交的地方的第一個字母等於綠色的第一個字母……那麼中間至少有一個以s[1..n-next[n]]為迴圈節的字串。

證明:假設有這樣的例子ABAB……AB使得字首字尾有重疊並且中間重疊部分不是由那種迴圈節組成,比如ABABCAB ,ABABACAB,那麼將字尾往前平移的時候不能與字首重合。所以這種情況中間一定是迴圈節,並且和兩邊相等。

②如果單獨的藍色和綠色部分不相等而中間有重合,比如A.........C=>AA......CC顯然矛盾

這時n % (n-next[n])==0,並且迴圈節為n-next[n]

週期的個數為n / (n-next[n])

另外所有可能的週期長度為 n-next[n],n-next[next[n]],n-next[next[next[n]]]...

因為如果後面已經匹配了,相當於再在前next[x]個數中找一個相同的迴圈節,最小迴圈節*2 *3 *4之類的。。。如果再能被整除的話就是一個新的週期了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
int next[1000010];
int n,t;
char s[1000010];

void getnext()
{
    int j=-1;
    next[0]=-1;
    for (int i=1;i<n;i++)
    {
        while (j>-1 && s[j+1]!=s[i]) j=next[j];
        if (s[j+1]==s[i]) j++;
        next[i]=j;
    }
}
int main()
{
    scanf("%s", s);
    while (s[0]!='.')
    {
        n=strlen(s);
        getnext();
        t=next[n-1]+1;
        if (n % (n-t)==0) printf("%d\n", n / (n-t)); else printf("%d\n", 1);
        scanf("%s", s);
    }
}