kmp演算法的理解與實現

2021-08-25 20:05:49 字數 2614 閱讀 8189

kmp演算法曾被我戲稱為看毛片演算法,當時笑噴......大三那個時候硬著頭皮把演算法導論的kmp演算法啃完,弄懂了kmp演算法

的原理,甚至還寫出了**,這幾天再次溫習的時候,發現忘得比較徹底。我總結,學演算法不能只對著書本學理論,而應該

用自己的理解去看清演算法的本質,最好用文字把你的理解記錄下來,這樣才能做到活學活用,而且不容易忘。寫這篇部落格就是

想把自己這幾天的思路記下來。

一 kmp演算法為什麼比傳統的字串匹配演算法快

假設文字t = y1y2y3....yn, 模式 p = p1p2p3...pm, 傳統的匹配演算法把位移為0,1,...n-m時的文字依次跟p比較,每次比較最多花費o(m)的時間,演算法的複雜度為o((n-m+1)*m)。這種演算法沒有利用匹配過的資訊,每次都從頭開始比較,速度很慢。而kmp演算法充分利用了之前的匹配資訊,從而避免一些明顯不合法的位移。加快匹配過程。來看乙個例子:

#########

000***x000

######                       文字t

|<---- s ---->|000***x000

~~~                              模式p

假設位移為s時,t和p匹配了紅色部分的字元,即匹配到了模式p的前10個字元,如果按照傳統的匹配方法,下一步就是從位移s+1開始比較,而kmp演算法則直接從位移s+7開始比較,而且斷定:位移s+7對應的串和模式p的前3個字元是相同的,可

以不用比較,直接從第4個字元開始比較,這種跳躍式的匹配是不是比傳統匹配方法快很多,如下圖所示:

#########

000***x000

######                       文字t

|<-------- s+7-------->| 000***x000

~~~               模式p

那麼kmp是如何實現這種跳躍的呢?注意到紅色部分的字元,即模式p的前10個字元,有乙個特點:它的開始3個字元和末尾

3個字元是一樣的,又已知文字t也存在紅色部分的字元,我們把位移移動 10-3 = 7個位置,讓模式p的開始3個字元對準文字

t紅色部分的末尾3個字元,那麼它們的前3個字元必然可以匹配。

二 構造字首陣列

上面的例子是文字t和模式p匹配了前面10個字元的情況下發生的,而且我們觀察到模式p的字首p10中,它的開始3個字元和末尾3個字元是一樣的。如果對於模式p的所有字首p1,p2...pm,都能求出它們首尾有多少個字元是一樣的,當然相同的字

符數越多越好,那麼就可以按照上面的方法,進行跳躍式的匹配。

定義:pi表示模式p的前i個字元組成的字首, next[i] = j表示pi中的開始j個字元和末尾j個字元是一樣的,而且對於字首pi來說,這樣

的j是最大值。next[i] = j的另外乙個定義是:有乙個含有j個字元的串,它既是pi的真字首,又是pi的真字尾

規定:next[1] = next[0] = 0

next[i]就是字首陣列,下面通過1個例子來看如何構造字首陣列。

例子1:cacca有5個字首,求出其對應的next陣列。

字首2為ca,顯然首尾沒有相同的字元,next[2] = 0

字首3為cac,顯然首尾有共同的字元c,故next[3] = 1

字首4為cacc,首尾有共同的字元c,故next[4] = 1

字首5為cacca,首尾有共同的字元ca,故next[5] = 2

如果仔細觀察,可以發現構造next[i]的時候,可以利用next[i-1]的結果。假設模式已求得next[10] = 3,如下圖所示:

000#***000         字首p10

000                        末尾3個字元

根據字首函式的定義:next[10] = 3意味著末尾3個字元和p10的前3個字元是一樣的

為求next[11],可以直接比較第4個字元和第11個字元,如下圖所示:藍色和綠色的#號所示,如果它們相等,則

next[11] = next[10]+1 = 4,這是因為next[10] = 3保證了字首p11和末尾4個字元的前3個字元是一樣的.

000#***000

#       字首p11

000#                      末尾4個字元

所以只需驗證第4個字元和第11個字元。但如果這兩個字元不想等呢?那就繼續迭代,利用next[next[10] = next[3]的值來求

next[11]。**如下:

void compute_prefix(int *next, char *p)

}

三 模擬kmp的查詢過程

這裡實現的演算法與演算法導論中的不一樣,我覺得這種方法更加直觀,思路就是模擬第1部分介紹的方法,每次匹配的時候

都利用上一次匹配資訊,對於模式p,從第next[q]個字元開始比較,對於文字t,用乙個變數s指示將要從哪個位置開始比較

,迭代開始之前,就從s這個位置開始。

void kmp_match(char *text, char *p, int *next)

}}

四 測試

int main()

KMP 演算法原理解析與 C 實現

kmp 演算法包含兩個部分 利用 pattern 構建 next 陣列 利用 next 資料去匹配長字串 使用 kmp 演算法的好處是,對於被匹配的長字串,我們始終不需要往回走。next 陣列中的每乙個元素,比如說 next i 代表的是在 pattern 中第 i 個位置之前存在的字首和字尾相等的...

KMP演算法的理解

串的模式匹配演算法主要有兩種,一是簡單模式匹配,而是kmp演算法。簡單模式匹配演算法很容易理解,每一次從主串的第乙個位置起和模式串的第乙個字元開始比較,如果相等就按照順序一直比下去,如果不相等就把模式串和第二個位置開始繼續進行比較,最後若匹配成功則返回主串中與模式串匹配的第乙個字元的位置,雖然簡單易...

理解KMP演算法

由暴力匹配引入kmp演算法 問題 有乙個文字串s,和乙個模式串p,現在要查詢p在s中的位置。如果用暴力匹配的思路,並假設現在文字串s匹配到 i 位置,模式串p匹配到 j 位置,則有 示例 上面s,下面p 比如從a這裡開始匹配上了 一直這樣匹配下去 到這裡匹配不上了 就回滾回去重新開始 這種回滾的問題...