KMP算法詳解

問題:已知兩個字符串分別爲S、P如下圖,判斷s串中存在幾個p串,並輸出其起始位置?

目錄

         兩種算法的區別 

         暴力解決

         KMP算法

       


兩種算法的區別:

黃線爲已經匹配的部分,兩段綠線爲c之前相同的前後綴,當c失配時,分別從箭頭位置開始匹配的效果是完全相同的。

暴力是從第一個箭頭開始,KMP是從第二個開始,這就是兩中方法的差別。(不知道能不能看懂)

一、暴力解決:

複雜度:O(N*M

1、abab匹配成功,但當 i=4 (j=4) 時d與c失配。

2、令j=0,i=1重新開始匹配,當i=1(j=0)時失配,繼續移動。

3、重複上面的步驟,知道最後匹配成功,輸出起始位置。

代碼模板:

void Search(string a,string p)
{
    int la=s.size(),lp=p.size();
    for(int i=0;i<la;i++){
        for(int j=0;j<lp;j++){
            if(a[i]!=p[j]){
                break;
            }
            if(j==lp-1){
                cout<<i<<endl;
            }
        }
    }
}

上面的複雜度確實很高,KMP算法的優點就是用線形的時間複雜度解決這個問題。

二、KMP算法

複雜度:O(N+M) 

1、同樣abab匹配成功,但當 i=4 (j=4) 時d與c失配。

2、此時令j=Next[j],即j=Next[4]=2,i不變,繼續比較,此時d與a失配。(先不要關心Next[4]=2什麼意思,求Next數組傳送門

 

3、重複上面的步驟,直到最後完全匹配爲止。

比較上面的兩種方法應該能看出複雜度的曲別。

Next數組含義:

以p串爲例子,它的每個座標對應的Next數組的值如下:

Next數組的含義:Next[4]=2的含義就是 [0,3] 這個子串的前綴與後綴最大相同的字符數目爲2。

求解Next數組:

先上代碼(找了很多天,這個算是最好用的了)      感謝:Venishel

///p[k]爲前綴,p[i]爲後綴
void getNext(string p,LL lp,LL Next[]){
    LL k=0;
    for(LL i=1;i<lp;i++){
        while(k&&p[i]!=p[k]) k=Next[k];
        if(p[i]==p[k]) k++;
        Next[i+1]=k;
    }
}

KMP算法匹配:

感謝: v_JULY_v

該字符對應的Next 值會告訴你下一步匹配中,j應該跳到哪個位置(跳到Next [j] 的位置)。如果Next [j] 等於0,則跳到p串的開頭,若Next [j] 不爲0,代表下次匹配跳到Next[j]的位置,而不是跳到開頭,且具體跳過了j-Next[j] 個字符。

像下面的這種情況:

1、現在d與c失配:

2、Next[j]=2,即j跳到2的位置。

代碼: 

void kmp(string a,string p,LL la,LL lp){
    LL j=0;
    for(LL i=0;i<la;i++){
        while(j&&a[i]!=p[j]) j=Next[j];
        if(a[i]==p[j]) j++;
        if(j==lp) cnt++,j=0;
    }
}

KMP完整代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAX=1e6;
LL Next[MAX+5],cnt;
void getNext(string p,LL lp,LL Next[]){
    LL k=0;
    for(LL i=1;i<lp;i++){
        while(k&&p[i]!=p[k]) k=Next[k];
        if(p[i]==p[k]) k++;
        Next[i+1]=k;
    }
}
void kmp(string a,string p,LL la,LL lp){
    LL j=0;
    for(LL i=0;i<la;i++){
        while(j&&a[i]!=p[j]) j=Next[j];
        if(a[i]==p[j]) j++;
        if(j==lp) cnt++,j=0;
    }
}
int main()
{
    string a,p;
    cin>>a>>p;
    LL la=a.size(),lp=p.size();
    getNext(p,lp,Next);
    kmp(a,p,la,lp);
    cout<<cnt<<endl;
    return 0;
}

 

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