KMP 演算法,分析及遞迴實現

2021-08-22 18:19:53 字數 3770 閱讀 9095

額, 因為在準備考研,基本上不部落格了,重新遇到了kmp演算法,心血來潮,所以就來搗鼓一下了,沒錯,我來裝 x 的 ,我的麒麟臂在早已飢渴難耐了。

想必來看kmp演算法的人,都知道kmp演算法是拿來幹嘛的,實現字串模式匹配的高效演算法,不過拿去其他模式匹配應該也可以,只要你想得到。現在再重述一遍演算法思想:

假設已有: 目標串 t[0…n-1],模式串 p[0…m-1] ,注:(t[i…j] 理解成tit

i+1.

..tj

t_t_...t_

ti​ti+

1​..

.tj​

, $ 0 \lt i \lt n-1$),p也如此理解。

不失一般性,從 t 中的第 s ( 0≤s

≤n−1

0 \le s \le n-1

0≤s≤n−

1 ) 個位置與 p 中 第 j ( $ 1 \le j \le m-1$ ) 個位置匹配失敗。 則此時有 t[s…s+j-1] = p[0…j-1],按照樸素演算法,下一步是將從 ts+

1t_

ts+1

​ 開始與 p 比較,若匹配成功將有 t[s+1…s+m] = p[0…m-1],也即有 t[s+1…s+j-1] = p[0…j-2]。

綜合,已有條件:

t[s…s+j-1] = p[0…j-1] →ts

ts+1

..ts

+j−1

=p0p

1..p

j−

1\to t_t_..t_ = p_p_..p_

→ts​ts

+1​.

.ts+

j−1​

=p0​

p1​.

.pj−

1​t[s+1…s+j-1] = p[0…j-2] →ts

+1ts

+2..

ts+j

−1=p

0p1.

.pj−

2\to t_t_..t_ = p_p_..p_

→ts+1​

ts+2

​..t

s+j−

1​=p

0​p1

​..p

j−2​

聯合1、2可得, p0p

1..p

j−2=

p1..

pj−1

p_p_..p_ = p_..p_

p0​p1​

..pj

−2​=

p1​.

.pj−

1​,即p[0…j-2] = p[1…j-1]

上面的推導表明,只有當 p[0…j-2] = p[1…j-1] 成立時,才會有 t[s+1…s+m] = p[0…m-1]。 如果 p[0…j-2] $ \ne$ p[1…j-1] ,必然導致 t[s+1…s+m] $ \ne$ p[0…m-1]。

所以自然就不用進行t從s+1開始的模式匹配,而是嘗試從s+2開始,如果等式不成立, 就繼續嘗試s+3開始, 直到第k 次, 首次使得:

∗ ∗認

住這傢伙

∗∗p[

0..k]=

p[j−

k+

1..j−1

]**認住這傢伙**p[0..k] = p[j-k+1..j-1]

∗∗認住這家

夥∗∗p

[0..

k]=p

[j−k

+1..

j−1]

成立。這個時候觀察 p[j-k+1…j-1] 有:

t[s+j-k+1…s+j-1] = p[j-k+1…j-1] = p[0…k] →

\to→ t[s+j-k+1…s+j-1] = p[0…k] — 公式 3

因為之前是在第 j 個匹配失敗的,然後又確認了上的 3 式的成立,所以我們很很愉快的從 pk+

1p_

pk+1

​ 與 ss+

js_

ss+j

​ 開始比較了。後面的比較也是不斷的重複上述的步驟。可以發現比較過的t是不再回溯的,所以效率很高。下面將以next(j) 表示第j個匹配失敗後,返回的k值,即下一次要與 t[s+j] 比較的 p[k], 這裡明確一點, 返回的k值, 必需滿足 p[0…k] = p[j-k+1…j-1] 。

接著,我們將問題轉換到了找 k 值,假設已經知道 next(j) = k,現在要求 next(j+1) 。

對於next(j) = k ,明確知道 k 代表的是p[ j ] 與t[s+j]匹配失敗後,下乙個要與 t[s+j] 匹配的位置, 至於p[ k ] 與 t[ s+j ] 是否相等,是不知道的,根據 k 值,我們有:

p[j-k…j-1] = p[0…k-1] = t[s+j-k…s+j-1]

現在,假設在第 j+1 次匹配失敗, 則有 p[0…j] = t[s…s+j] →

\to→ p[j-k…j] = t[s-k…s+j] 。

此時分兩種情況:

(1) p[k] = p[j]

觀察 p[0…k-1] 和 p[j-k…j] ,已有 p[j-k…j-1] = p[0…k-1] ,若 p[k] = p[j] 則有 p[0…k] = p[j-k…j] ,即我們找到了第 j+1 次匹配失敗時,應該返回下乙個與 t[s+j+1] 比較的位置, next(j+1) = k+1 。

(2) p[k] != p[j]

但是我們有 p[j-k…j-1] = p[0…k-1] , 問題模擬成 p[k] 與 p[j] 匹配失敗, 則需要在找到乙個 h = next(k) ,使得 p[0…h-1] = p[k-h…k-1] ,再比較 p[h] 與 p[j] ,若相等,返回 h+1,此時有p[h+1] 與 t[s+j+1] 進行匹配。若p[h] != p[j] ,繼續重複步驟(2),直至傳入 next() 的引數為0時,返回 -1 表示失敗,即p[0…j-1] 中沒有乙個等於 p[j] 的,此時 s := s+1 ,目標串向前推進一步。

下面將根據上述分析過程,寫出遞迴演算法計算第j個位置匹配失敗後應該返回的k值。

演算法將以兩種形式給出,第一種是偽**,第二種是c++**。

偽**:

next(p, j)

if j == 0

return -1

k = next(p, j-1)

while k != -1

if p[k] = p[j-1]

return k+1

k = next(p, k)

return k+1

c++**:

//教材**

next(int next)

else k = next[k]; //p[j-1] != p[k] , 往p[0..k-1] 中找到與 p[j-1]相等的位置 }}

int next(string p, int j)

while( k != -1 );

return k + 1;

}//配上乙個kmp, 返回的是1..n形式位置。嗯...沒有在怎麼測試過。

int kmp(string t, string p)

{ int s, j ;

s = j = 0;

while( abs(j) < p.length() && s < t.length() )

{cout《遞迴演算法雖然效率低,每次都要重新計算乙個 next 值,但是更加的直觀,讓我們了解kmp是如何實現的,有時間+有興趣就將遞迴改寫成非遞迴,並用陣列將結果存起來,當然你可以用乙個小技巧,呼叫next()一次,結果存一起,也是可以的拉。一開始,還以為能夠很好的 control 住。最後…希望能有點幫助把…還有參考了 殷人昆主編的《資料結構c++版》。

KMP演算法及python實現

kmp演算法是一種在字串匹配中應用十分廣泛 也十分高效的演算法,就是查詢模式串 子串 在目標串 主串 現的位置,具體的問題可參考leetcode 28.實現strstr 題面如下圖所示。最暴力的演算法就是 模式串的第0位與目標串的第0位進行比較,如果匹配,則比較模式串的第1位與目標串的第1位 如果不...

KMP演算法及python實現

knuth morris pratt kmp 演算法是解決字串匹配問題的經典演算法,下面通過乙個例子來演示一下 給定字串 bbc abcdab abcdabcdabde 檢查裡面是否包含另乙個字串 abcdabd 從頭開始依次匹配字元,如果不匹配就跳到下乙個字元 直到發現匹配字元,然後經過乙個內迴圈...

KMP演算法分析

根據博主july的所載,記錄個人理解心得 紅色部分為個人理解 1.kmp演算法流程 假設現在文字串s匹配到 i 位置,模式串p匹配到 j 位置 1.如果j 1,或者當前字元匹配成功 即s i p j 都令i j 繼續匹配下乙個字元 當s i p j 時,說明模式串j前面的字元都與文字串i前面對應的字...