1. 程式人生 > >跳出課本學KMP演算法

跳出課本學KMP演算法

KMP演算法用於串的模式匹配,主串S,子串T(也叫模式串),模式匹配意思是從S中找出跟T一樣的子串,就是說判斷S是否包含T,時間複雜度O(m+n),實現這個演算法關鍵有兩步,第一步,求next陣列,第二步KMP主演算法

如何理解next陣列,**next[j]表示當子串T中第j個字元與主串中相應字元“失配”時,在子串T中需要重新和主串S中該字元進行比較的字元的位置。**例子:
求:主串S=“ababcabcacbab”,子串T=“abcac”KMP演算法匹配過程
假如說已經求出來next陣列(最後再研究next陣列怎麼求)如下:
在這裡插入圖片描述
下面進行KMP匹配,i與j分別標記主串與子串位置,且都從1開始
0812397.png)


如上圖,我們從主串S第1位a開始對應匹配,匹配時候發現i等於3,j等於3時候不匹配,怎麼辦呢?一般我們都會從主串S第2位b開始再和abcac逐個匹配,但有了next陣列就不必那樣做了,此時i不必退到第2位b處,應保持不動,主串不動,子串那必須行動行動,因為不匹配位置j=3,根據next陣列說法,next[j]表示當子串T中第j個字元與主串中相應字元“失配”時,在子串T中需要重新和主串S中該字元進行比較的字元的位置,此時next[3]=1,故j移動到T的第1位,即用T第1位與Si所指的第3位繼續匹配比較,如下:
在這裡插入圖片描述
可以看出,再次匹配時,i=7,j=5時候有不匹配了,此時再處理處理,保持i=7不動,j變為:j=next[j],此時j等於2,如下:
在這裡插入圖片描述

接下來按照以上步驟一步步來(能看出來此時其實已經匹配好了)

所以KMP匹配程式碼就容易寫了(純C):

int KMP(char  S[], char  T[], int pos)
{
    i=pos; j=1;//post表示從主串什麼位置開始,一般情況下都是從1開始的
    while(i<=strlen(S)-1&&j<=strlen(S)-1)//strlen(S)-1是實際長度,S[0]不是真正字元內容
    {
        if (j==0||S[i]==T[j])//匹配到了的話繼續往前走直到遇到不匹配的或匹配成功了
        {
            i++
; j++; } else { j=next[j];//當子串T中第j個字元與主串中相應字元“失配”時,在子串T中需要重新和主串S中該字元進行比較的字元的位置 } } if (j>=strlen(S)) return i-T[0];//匹配位置 else return 0; }

還有一個遺留問題:如何求next
在這裡插入圖片描述
觀察此表,發現
next[j]表示前j-1個字元前後綴失配位置
即next[j]表示前j-1個字元前後綴匹配數目+1,基於此:

void get_next(char T, char  next[ ]{
    i=1; next[1]=0; j=0;
    while(i<T[0])
    {
        if (j==0||T[i]==T[j])
            i++;
        j++;
        next[i]=j;
        else
            j=next[j];
    }
}