1. 程式人生 > >終於弄懂KMP演算法了

終於弄懂KMP演算法了

看了上面的文章,你肯定大概明白了KMP的運作原理,但是你可能對於文章提到的“部分匹配值”的又來還存在疑惑,那麼請繼續往下看:

我們先丟擲兩個問題,當目標字串i指標與模式字串j指標失配時:
1、當母串和模式串不匹配時,i指標為什麼不需要回溯?
2、當母串和模式串不匹配時,i指標不回溯,那麼j指標應該移動到哪?
通過解釋第1個問題,我來引出第2個問題。(其實第1個問題,你不明白,不影響你研究第2個問題,你也可以理解了第2個問題後來分析第1個問題。)
對於母串S和模式串T,如果母串的指標i處與模式串的j處失配,假設母串存在回溯i到back(i) (匹配前的i < back(i) < i),有與模式串的從0開始的模式串有一段一樣長的“段落”,這個母串的段落只有是S[back(i)] 至S[i-1]才有意義,如果從S[back(i)] 至S[i-1]的過程中就已經不匹配了,那回溯至這個back(i)就更沒有必要了。

即S[back(i) …i-1]=T[0…x] ,x = i-1-back(i)。又因為前面失配時存在同樣長度的段落,即S[back[i]…i-1] = T[ j -1-x… j-1],所以T[0…x] = T[j-1-x …j-1]

這其實就轉化成了i不變,j指標移動到x+1(後面討論用next[j] 表示 x +1 )的問題。

看到這兒,雖然我們應該已經理解了KMP演算法的運作原理,那麼如何求模式傳T匹配失敗時的指標移動值next[j]呢:

C code--
void GetNextArrayForKMP(char *sub_str, int *next)
{
  int i = 0
, j = -1, len = strlen(sub_str); next[0] = -1; // printf("i = 0; j = -1; next[0] = -1 \n"); while (i < len - 1) { if (j == -1 || sub_str[i] == sub_str[j]) { ++i; // printf("++i = %d; ", i); ++j; // printf("++j = %d; ", j); next[i] = j; // printf("next[%d] = %d; "
, i, j); } else { j = next[j]; // printf("j = next[j] = %d ; ", j); } // printf("\n"); } }

在kmp函式中,next使用是因為出現了失配現象:即當前模式串和主串字元不等,但是他們前面的字元都匹配。失配時模式串當前位置pj,主串的當前位置si。
失配時假設模式串不進行回溯,而調到k位置,即前面的k-1個字元和主串當前位置si的前k-1個字元匹配,然而主串的k-1字元和模式串當前位置pj的k-1位置匹配。因此模式串前k-1個字元和模式串當前位置pj的前k-1個字元匹配,即:P1…Pk-1 = Pj-k+1…Pj-1
求next的函式跟kmp函式非常類似。
求next陣列時候,模式串sub_str既是主串又是模式串。
程式碼中i指向的是主串,j指向的是模式串,當sub_str[i] != sub_str[j]時候,說明出現了失配現象,模式串需要改變位置,j = next[j];