字串匹配 KMP 演算法

2021-10-07 15:56:42 字數 3827 閱讀 8885

python **

後記

字串匹配是電腦科學中最古老、研究最廣泛的問題之一。乙個字串是乙個定義在有限字母表∑上的字串行。字串匹配問題就是在乙個大的字串t中搜尋某個字串p的所有出現位置。其中,t稱為文字,p稱為模式,t和p都定義在同乙個字母表∑上。

模式串匹配演算法發展得比較慢,實用的出現很晚。kmp 演算法是大部分書介紹完 bf 演算法後的簡化方法,說容易也是說難也行,其實理解了真的很容易操作。

kmp 演算法利用的是模式串本身的性質,與文字串無關,這點一定要記住。如果模式串中存在相似部分,比如 abcabdabe,abc、abd 和 abe 三部分都很相似,kmp 演算法利用這個特點提高匹配效率。

串匹配時某個位置不匹配時,需要移動指標重新匹配。bf 演算法的問題就在於匹配失敗時文字串只前進一位,而模式串回到開頭匹配,造成不必要的浪費。比如下面情況:

如果是 bf 演算法,下一輪操作,是不是很傻:

既然 ab 和 ac 有相似部分 a,如果 ac 的 a 不能匹配,那麼 ab 的 a 也必然不能和文字串這個位置匹配,可以直接跳過:

同時,如果 ac 的 c 匹配失敗,有沒有可能這個位置是 ab 呢:

是不是很快了呢?一下跳躍那麼多。所以問題在於要移動多少。

針對模式串每個位置匹配失敗的移動距離,需要乙個額外的陣列記錄,就叫 next 陣列。設模式串長度 m,陣列長度即為 m。在開始匹配前,進行模式串處理,找最長相似真字首和真字尾。

對於 abcabdabe 這個串,next 陣列如圖。next[0]=-1 是固定操作,第一位置匹配失敗肯定要移動文字串位置。對於 ab、abc、abca 三部分,沒有相似成分,所以匹配失敗了直接從頭再來;abcab 出現了相似成分 a*,如果第二處 a* 匹配失敗則利用第一處 a* 嘗試匹配;abcabd 有相同成分 ab* 所以後面的 ab* 匹配失敗則嘗試前面的 ab* 匹配…

總結為一張表如上,注意相似真前字尾長度放在的是 next 陣列下乙個位置,因為要比較的是相似部分後面的不同部分。

具體到計算方法:用兩個指標,設為 i 和 j,初始 i=0、j=-1。next[0]=-1,每個位置的 next 值與前一位有關,要麼0要麼前一位 +1;當指標指向字元相同時兩個指標都自增,並且 next 的值自增並放到下一位置;如果不同 j 回溯,回溯到的位置是 next 的值,如果 j 為 -1 就同時自增。

雖然計算 next 陣列然後快速移動提高了效率,但還不夠。考慮一種極端的情況 aaaaaab,正常來說 next 是 [-1, 0, 1, 2, 3, 4, 5],但前面的一串 a 有乙個匹配失敗其實都可以跳過全部 a;比如上面的串 abcabdabe,後面兩個 ab 匹配 b 失敗都會嘗試和第乙個 ab 的 b 匹配,因為符合 a*,顯然還可以加速。那麼計算 next 陣列時多乙個條件:指標自增後如果字元相同就再回溯,如果不同則按原來操作。比如 abcab… 時指標分別指到兩個 a,自增後發現兩個 b 也是相同字元,讓後乙個 b 的回溯位置和前乙個 b 的回溯位置相同。

看**會更容易理解。

def

kmp_match

(text, pat)

:next

= get_next(pat)

a =0 b =

0while a < text.__len__(

)and b < pat.__len__():

if b ==-1

or text[a]

== pat[b]

: a +=

1 b +=

1else

: b =

next

[b]if b == pat.__len__():

print

("found:"

, a-b)

a = a-b

b =-1

"""未改進的 next

def get_next(pat):

next = [-1, ]

a = -1

b = 0

while b < pat.__len__()-1:

if a == -1 or pat[a] == pat[b]:

a += 1

b += 1

else:

a = next[a]

return next

"""## 改進的 next

defget_next

(pat)

:next=[

-1,]

j =-1

i =0while i < pat.__len__()-

1:if j ==-1

or pat[j]

== pat[i]

: j +=

1 i +=

1if pat[j]

!= pat[i]

:next

else

:next

next

[j])

else

: j =

next

[j]return

next

kmp_match(

input()

,input()

)

#include

#include

#include

void

getnext

(short

* next,

char

* pat)

;int

main

(int argc,

const

char

* ar**)

}free

(next)

;return0;

}/*未改進的

void getnext(short* next, char* pat)

else

j = next[j];

}}*/

void

getnext

(short

* next,

char

* pat)

else

j = next[j];}

}

嚴格的數學證明沒寫,不難懂但有點抽象,第一次看是會看不懂。這個演算法看懂前和看懂後是兩種感覺,所以需要點時間理解。本篇寫得一般,實在不知道怎麼解釋最清晰。

其實 kmp 演算法用於串匹配不太常見,它比較適合無序又有規律的串。比如在網頁搜尋 tomorrow,這詞沒有一處可以 kmp…

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的第乙個字元,依次比較下去,直到得出最後的匹配...