字串匹配 KMP演算法

2022-08-23 19:06:10 字數 2375 閱讀 7246

關於kmp演算法的分析,我覺得這兩篇部落格寫的不錯:

下面的筆記也是參考了這兩篇部落格的。

kmp演算法是最有名的字串匹配演算法了。它是bf演算法

的改進版,至於是如何改進的,先引用上述第二篇部落格裡的一段話:

「在繼續分析之前,咱們來思考這樣乙個問題:為什麼快排或者堆排序比直接的選擇排序快?直接的選擇排序,每次都是重複地比較數值的大小,每掃瞄一次,只得出乙個最大(小值),再沒有其它的成果。而快排,每掃一次,將資料分成了兩部分,右邊的資料都大於左邊的資料,所以在下一次比較的時候,這兩部分元素之間就不用比較了。再看堆排序,建堆的過程也是o(n)的比較,但比較的結果得到了最大(小)堆這種三角關係,之後的比較就不用再每乙個都需要比較了。

由上述思考,咱們總結出了一點優化的歸律:採用一種簡單的資料結構或者方式,將每次重複性的工作得到的資訊記錄得盡量多,方便下一次做同樣的工作,這樣將帶來一定的優化。」

總結上面這段話的核心思想,就是把迴圈中要重複做的工作提取到迴圈外完成,從而提高效率。

下面用乙個例子演示kmp匹配的過程。其中涉及的三個資料如下:

source:源串,即要匹配的母船

pattern:模式串,即子串

next陣列:其每個元素對應pattern的每個字元,表示當該字元pattern[j]不匹配source[i]時,應該從pattern的哪個下標開始重新匹配當前的source[i],具體求法後面介紹

本例中source為abcabcabde,pattern為abcabd,第一次匹配,前5個字元均匹配成功,匹配第6個字元時如下:

此時發現source[5]和pattern[5]不匹配,bf演算法的做法是從source[1]開始從新匹配pattern,即像下面這樣:

而kmp演算法則不會再去重新比較source[1...4]了,它會利用next的資訊調整pattern。因為next[5]=2,所以下一次匹配將當前的source[5]和pattern[2]比較,即像下面這樣:

這時發現source[5]和pattern[2]匹配,繼續往下比較,直至pattern中所以元素都比較完了,如下:

此時已經在母串中找到了子串,i=9,m=6,所以返回的下標為i-m=3。

這樣整個匹配過程就完了,感覺不過癮嗎,前面兩篇推薦的部落格裡有更長的匹配串分析。

把上面的過程翻譯成**,也就是kmp演算法的實現,如下:

int kmp(char *source, char *pattern)

else

}free(next);

if (j == m)

else

}

下面來看next陣列是如何求得的。

如前所述,next陣列裡儲存的就是pattern[j]與母串中的字元不匹配時,該用哪個下標繼續和這個字元匹配。若next[j]=k,則有:pattern[0...k-1] = pattern[j-k...j-1],而且這個k是最大的,即不會有pattern[0...k] =pattern[j-k-1...j-1]。

反過來想,如果已經知道了pattern[0...k-1] = pattern[j-k...j-1],next[j]是多少呢?要分兩種情況考慮:

1. pattern[k] != pattern[j],則next[j] = k

2. pattern[k] = pattern[j],則next[j] = next[k]

第二種情況是因為,若pattern[k] = pattern[j]時,也使next[j]=k;則在pattern[j]於母串不匹配的情況下,再拿pattern[next[j]]即pattern[k]去比較的話,肯定也是不匹配的。這是隱藏的重複工作。使next[j] = next[k]就能避免這種重複工作。

那麼怎麼又能保證pattern[j]不會等於pattern[next[k]]呢?不急,一步一步想:最開始有next[0]=-1,如果pattern[1]=pattern[0],那麼由規則2,next[1]=next[0]=-1;再如果pattern[2]=pattern[1],那麼next[2]=next[1]=-1。看到了嗎,三個一樣的字元,如果是next依次為前乙個元素的情況,最終都會是最前面那個next值,這就保證了最大的減少重複工作。

好吧,上面的解釋很挫,意會吧。其實就跟離散樹一樣,同乙個集合的元素都有相同的根。

有了上面的規則後,求next陣列就比較方便了。用動態規劃的策略很好實現:

void next(char *pattern, int *next)

else

}else}}

是不是感覺和kmp的框架很像,其實就是一回事,都是子串匹配,都利用了next陣列。

不同之處在於:kmp是source匹配pattern,next是一直從pattern的頭部開始匹配後面所有的序列;在next裡面,next陣列還沒有完全生成,是利用前面生成的next加上匹配結果生成下乙個next,這就是動規的策略。

KMP演算法 字串匹配

kmp演算法基本思想 我們在用常規的思想做 字串匹配時候是 如 對如 字元如果 t abab 用p ba 去匹配,常規思路是 看 t 第乙個元素 a 是否 和p 的乙個 b 匹配 匹配的話 檢視各自的第二個元素,不匹配 則將 t 串的 第二個元素開始 和 p 的第乙個匹配,如此 一步一步 的後移 來...

KMP字串匹配演算法

kmp核心思想 計算模式串的next陣列,主串的索引在比較的過程中不回朔 ifndef kmp h define kmp h class kmp endif include kmp.h include include include using namespace std int kmp calcu...

KMP字串匹配演算法

在介紹kmp演算法之前,先介紹一下bf演算法。一.bf演算法 bf演算法是普通的模式匹配演算法,bf演算法的思想就是將目標串s的第乙個字元與模式串p的第乙個字元進行匹配,若相等,則繼續比較s的第二個字元和p的第二個字元 若不相等,則比較s的第二個字元和p的第乙個字元,依次比較下去,直到得出最後的匹配...