關於KMP的一點思考

關於KMP的一點思考

KMP的\(next\)數組的性質很精妙,有必要開一個坑學習一下

Part 1 啥是next

\(next[i]\)表示對於\(pre_i\)這個字符串,這個摳出來的字符串本身後綴和前綴相等的最長長度。是一個自變量只和這個子串有關的函數。這點很重要

由於保證了是最長長度,這個數有一些優良的性質,常常在關於一個串的循環表示或者週期表示中發揮作用。

注意到這個\(next[i]\)雖然代表是這個最長長度,但是值得注意的是,由於字符串從1開始編號,所以這個值也是那個前綴的下標。

Part2 如何求next

邊界條件是,\(nx[1]=0\)。考慮我們若已經求得前面\(i-1\)的位置的\(nx\)值,現在如何求\(nx[i]\)

\(S[1\dots i-1]\)看做一個整體,現在我們在後面加入了一個字符\(S[i]=c\)

我們現在就是要在\(pre_{nx[i-1]}\)中截一個最大的位置\(p\),使得\(S[p+1]=c\),而\(p\)雖然是下標,但是由於從\(1\)開始編號那麼就同時就是這個串的長度,所以\(nx[i]=p+1\)。爲什麼是在\(pre_{nx[i-1]}\)中找呢?因爲我們要保證\(S[i-p+1...i]=S[1,p]\)

所以如何找\(p\)呢?由於我們要保證剛剛寫的這個等式,可以發現\(p\)一定是在\(G=(V,E),E=(x,nx[x])\)這樣的圖中和\(nx[i-1]\)聯通的到祖先的鏈上,所以我們一直暴力跳\(nx[]\)也就是遍歷這條鏈,直到第一次找到一個位置\(p\)使得\(S[nx[p]+1]=S[i]\)

但是你可能覺得這樣的複雜度是假的,下面我將證(復)明(讀)暴力跳\(nx[]\)遍歷的複雜度不超過\(O(n)\)

可以發現\(nx[i]\le nx[i-1]+1\),得證。

哈哈哈哈

其實就是,\(nx[i]\)的總增長是\(O(n)\)的,而且一次最多增長\(1\),所以在其間不斷跳的複雜度不超過\(O(n)\)。(總共只有這麼多\(nx\)給你跳啊!)

代碼:

    for(int t=2;t<=s2;++t){
        nx[t]=nx[t-1];
        while(nx[t]>0&&T[nx[t]+1]!=T[t]) nx[t]=nx[nx[t]];
        if(T[nx[t]+1]==T[t]) ++nx[t];
    }

Part 3 一些性質

由於我馬上就要咕咕咕所以

P3435 [POI2006]OKR-Periods of Words

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define getchar() (__c==__ed?(__ed=__buf+fread(__c=__buf,1,1<<18,stdin),*__c++):*__c++)

using namespace std;  typedef long long ll;   char __buf[1<<18],*__c=__buf,*__ed=__buf;
inline int qr(){
    register int ret=0,f=0;
    register char c=getchar();
    while(!isdigit(c))f|=c==45,c=getchar();
    while(isdigit(c)) ret=ret*10+c-48,c=getchar();
    return f?-ret:ret;
}
const int maxn=1e6+5;
char c[maxn];
int n,nx[maxn],cut[maxn];

inline void kmp(){
    for(int t=2;t<=n;++t){
        nx[t]=nx[t-1];
        while(nx[t]>0&&c[nx[t]+1]!=c[t]) nx[t]=nx[nx[t]];
        if(c[nx[t]+1]==c[t]) ++nx[t];
    }
}

int Find(const int&p){
    if(!nx[p]) return p;
    if(cut[p]) return cut[p];
    return cut[p]=Find(nx[p]);
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
    //freopen("out.out","w",stdout);
#endif
    scanf("%d%s",&n,c+1);
    kmp();
    ll ans=0;
    for(int t=1;t<=n;++t)
        ans=(ans+t-Find(t));    
    printf("%lld\n",ans);
    return 0;
}

[P4824 USACO15FEB]Censoring (Silver) 審查(銀)

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;  typedef long long ll;
const int maxn=1e6+5;
char S[maxn],T[maxn];
pair<int,int> stk[maxn];
int s1,s2,top,nx[maxn];

int main(){
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    scanf("%s%s",S+1,T+1);
    s1=strlen(S+1);
    s2=strlen(T+1);
    for(int t=2;t<=s2;++t){
        nx[t]=nx[t-1];
        while(nx[t]>0&&T[nx[t]+1]!=T[t]) nx[t]=nx[nx[t]];
        if(T[nx[t]+1]==T[t]) ++nx[t];
    }
    int p=0;
    for(int t=1;t<=s1;++t){
        while(p&&T[p+1]!=S[t]) p=nx[p];
        if(T[p+1]==S[t]) ++p;
        stk[++top]=(pair<int,int>){t,p};
        if(p==s2) top-=s2,p=stk[top].second;
    }
    for(int t=1;t<=top;++t) printf("%c",S[stk[t].first]);
    putchar('\n');
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章