字串匹配演算法之KMP演算法和BM演算法

2021-06-20 04:51:41 字數 2727 閱讀 9592

本文主要介紹kmp演算法和bm演算法,它們分別是字首匹配和字尾匹配的經典演算法。所謂字首匹配是指:模式串和母串的比較從左到右,模式串的移動也是從左到右;所謂字尾匹配是指:模式串和母串的的比較從右到左,模式串的移動從左到右。看得出來字首匹配和字尾匹配的區別就僅僅在於比較的順序不同。下文分別從最簡單的字首蠻力匹配演算法和字尾蠻力匹配演算法入手,詳細的介紹kmp演算法和bm演算法以及它們的實現。

首先來看一下字首蠻力匹配演算法的**(以下**從linux原始碼string.h中摳出),模式串和母串的比較是從左到右進行(strncmp()),如果找不到和模式串相同的子串,則從左到右移動模式串,距離為1(s++)。

char * strstr(register const char *s, register const char *wanted)

kmp演算法中的kmp分別是指三個人名:knuth、morris、pratt,其本質也是字首匹配演算法,對比字首蠻力匹配演算法,區別在於它會動態調整每次模式串的移動距離,而不僅僅是加一,從而加快匹配過程。下圖通過乙個直觀的例子展示字首蠻力匹配演算法和kmp演算法的區別,前文提過,這二者唯一的不同在於模式串移動距離。

上圖中,字首蠻力匹配演算法發現匹配不上,就向右移動距離1,而kmp演算法根據已經比較過的字首資訊,了解到應該移動距離為2;換句話說針對母串的下乙個匹配字元,kmp演算法了解它下回應該匹配模式串的哪個位置,比如上圖中,針對母串的第i+1個字元,kmp演算法了解它應該匹配模式串的第k+1個字元。為什麼會是這樣,這是因為母串的子串t[i-k, i]=aba,而模式串的子串p[0,k]=aba,這二者正好相等。所以模式串應該移動到這個位置,從而讓母串的第i+1個字元和模式串的第k+1個字元繼續比較。

那k值又是如何尋找?請注意上圖中,模式串位置j已經匹配上母串的位置i,也就是t[i-k, i] = p[j-k, j]=aba;根據前文的t[i-k, i] = p[0, k] = aba, 從而得出p[0, k] = p[j-k, j] = aba。通過觀察發現,就是在模式的子串[0, j]中尋找乙個最長字首[0,k],從而使得[j-k, j] = [0,k];

於是可以定義乙個jump陣列,jump[j]=k,表示滿足p[0, k] ==p[j-k, j] 的最大k值,或者表述為:如果模式串j+1匹配不上母串的i+1,那跳轉到模式串k+1繼續比較。有了這個jump陣列,就很容易寫出kmp演算法的偽**:

j:=0;

for i:=1 to n do

begin

while (j>0) and (p[j+1]<>t[i]) do j:=jump[j];[

if p[j+1]=t[i] then j:=j+1;

if j=m then

begin

writeln(\'pattern occurs with shift \',i-m);

end;

end;

kmp演算法中jump陣列的構建可以通過歸納法來解決,首先確定jump[1]=0;假設jump[j]=k,也就是p[0, k] == p[j-k, k],如果p[j+1] == p[k+1],那麼得出[0,k+1] = p[j-k, j+1],從而更加定義得出jump[j+1] = k+1;

如果p[j+1] != p[k+1],那就接著比較p[j+1] ?= p[k1+1],其中(jump[k] = k1),根據(jump[k]=k1)的定義,p[0,k1] == p[k-k1, k],根據(jump[j]=k)的定義,p[0, k] == p[j-k, k],根據這兩個等式,推出p[0, k1] == p[j-k1, j],如果此時p[j+1] == p[k1+1],則得出:jump[j+1] = k1 +1 = jump[k] +1。

如果p[j+1] != p[k1+1],繼續遞迴比較p[j+1] 和p[jump[jump[k]]+1] …. p[1];

如果依次比較都不相等,那麼jump[j+1] = 0;寫成偽**如下,可以看出其實就是模式串自我匹配的過程。

jump[1]:=0;

j:=0;

for i:=2 to m do

begin

while (j>0) and (p[j+1]<>p[i]) do j:=jump[j];

if p[j+1]=p[i] then j:=j+1;

jump[i]:=j;

end;

考慮模式串匹配不上母串的最壞情況,字首蠻力匹配演算法的時間複雜度最差是o(n×m),最好是o(n),其中n為母串的長度,m為模式串的長度。kmp演算法最差的時間複雜度是o(n);最好的時間複雜度是o(n/m)。

字尾匹配,是指模式串的比較從右到左,模式串的移動也是從左到右的匹配過程,經典的bm演算法其實是對字尾蠻力匹配演算法的改進。所以還是先從最簡單的字尾蠻力匹配演算法開始。下面直接給出偽**,注意這一行**:j++;bm演算法所做的唯一的事情就是改進了這行**,即模式串不是每次移動一步,而是根據已經匹配的字尾資訊,從而移動更多的距離。

j = 0;

while (j <= strlen(t) - strlen(p))

為了實現更快移動模式串,bm演算法定義了兩個規則,好字尾規則和壞字元規則,如下圖可以清晰的看出他們的含義。利用好字尾和壞字元可以大大加快模式串的移動距離,不是簡單的++j,而是j+=max (shift(好字尾), shift(壞字元))

先來看如何根據壞字元來移動模式串,shift(壞字元)分為兩種情況:

字串匹配演算法之kmp演算法

kmp演算法是一種效率非常高的字串匹配演算法,是由knuth,morris,pratt共同提出的模式匹配演算法,所以簡稱kmp演算法 在乙個字串中查詢另乙個字串時,會遇到如下圖的情況 我們通常的做法是從第乙個串a的下一位b再逐位比較,但這樣的做法非常低效。仔細思考一下發現,第乙個串已經匹配的部分就是...

字串匹配演算法之 KMP演算法

關於kmp演算法的描述,推薦一篇部落格 該部落格詳細的描述了kmp演算法原理。下面的 實現了kmp演算法 1 使用暴力窮舉法,kmp演算法完成字串匹配演算法 2 include iostream 3 include string 4 include vector 5 using namespace ...

字串匹配之KMP演算法

以前零零散散做了些kmp的題目,一直也沒找出時間整理,這一段又開始研究字串了,就順便把kmp整理了一下。廢話不說了,我們直接入題。說到kmp,首先應該知道,它是一種字串查詢演算法,因為是由乙個姓k,乙個姓m和乙個姓p的人聯合發表的,所以就叫kmp演算法了。kmp演算法是一種線性時間的的字串匹配演算法...