模式匹配(kmp)及其優化

2021-10-05 11:37:38 字數 2767 閱讀 5454

對於兩個字串s,p。想要判斷s串中是否有模式串p,正常的思路是依次遍歷,看是否能夠匹配成功。但實際上這種有回溯的模式匹配效率往往不高,

故有此無回溯模式匹配演算法

乙個直接按照定義計算字首函式的演算法流程:

在乙個迴圈中以 的順序計算字首函式 的值( 被賦值為 )。

為了計算當前的字首函式值 ,我們令變數 從最大的真字首長度 開始嘗試。

如果當前長度下真字首和真字尾相等,則此時長度為 ,否則令 j 自減 1,繼續匹配,直到 。

如果 並且仍沒有任何一次匹配,則置 並移至下乙個下標 。

具體實現如下:

vector<

int>

prefix_function

(string s)

return pi;

}

注:

string substr (size_t pos = 0, size_t len = npos) const;

顯見該演算法的時間複雜度為o(n3) ,具有很大的改進空間。

第乙個優化

第乙個重要的觀察是 相鄰的字首函式值至多增加 。

參照下圖所示,只需如此考慮:當取乙個盡可能大的 時,必然要求新增的 也與之對應的字元匹配,即 , 此時 。

所以當移動到下乙個位置時,字首函式的值要麼增加一,要麼維持不變,要麼減少。

此時的改進的演算法為:

vector<

int>

prefix_function

(string s)

return pi;

}

在這個初步改進的演算法中,在計算每個 時,最好的情況是第一次字串比較就完成了匹配,也就是說基礎的字串比較次數是 n-1 次。

而由於存在 j = pi[i-1]+1 ( pi[0]=0 )對於最大字串比較次數的限制,可以看出每次只有在最好情況才會為字串比較次數的上限積累 1,而每次超過一次的字串比較消耗的是之後次數的增長空間。

由此我們可以得出字串比較次數最多的一種情況:至少 1 次字串比較次數的消耗和最多 n-2 次比較次數的積累,此時字串比較次數為 n-1 + n-2 = 2n-3 。

可見經過此次優化,計算字首函式只需要進行 次字串比較,總複雜度降為了 o(n2) 。

第二個優化:

在第乙個優化中,我們討論了計算 時的最好情況: ,此時 。現在讓我們沿著這個思路走得更遠一點:討論當 時如何跳轉。

失配時,我們希望找到對於子串 ,僅次於 的第二長度 ,使得在位置 的字首性質仍得以保持,也即 :

如果我們找到了這樣的長度 ,那麼僅需要再次比較 和 。如果它們相等,那麼就有 。否則,我們需要找到子串 僅次於 的第二長度 ,使得字首性質得以保持,如此反覆,直到 。如果 ,則 。

觀察上圖可以發現,因為 ,所以對於 的第二長度 ,有這樣的性質:

也就是說 等價於子串 的字首函式值,即 。同理,次於 的第二長度等價於 的字首函式值,

顯然我們可以得到乙個關於 的狀態轉移方程:

最終演算法¶

所以最終我們可以構建乙個不需要進行任何字串比較,並且只進行 次操作的演算法。

而且該演算法的實現出人意料的短且直觀:

vector<

int>

prefix_function

(string s)

return pi;

}

吐槽一下:雖然這個改進後的計算字首函式的演算法看起來很屌,但是在基準測試中發現,當模式串 的長度 不是很大(100 以內)的情況下,其實和樸素演算法也沒有什麼區別。

改進一下之後,變成了下面的next陣列和kmp演算法了

對模式串建立next陣列就是要根據next陣列來控制匹配,其含義是模式串的在第i項前字尾匹配的最大長度。

void

make_nexts()

i++; k++

; nexts[i]

= k;

}}

分析:這種nexts陣列的求法已經極大優化了原回溯模式匹配,但當第i項和第k項相同的時候,由於此時對於串s的第m項,s[m]!=p[i],所以s[m]!=p[k],故此時比較p[k]項一定不能匹配成功,故可以繼續改進。

void

get_nexts()

}

#include

#include

#include

using

namespace std;

const

int maxn =

10010

;int nexts[maxn]

;void

get_nexts

(string p)

}int

kmp(string p, string s)

if(j >=

(int

)p.size()

)return i - j;

return-1

;}intmain()

測試樣例1:

/*

aabcbabcaabcaababcaabcaababc

abcaababc

*/

結果1:

9
測試樣例2:

/*

aabbcbabc

bbc*/

結果2:

2

KMP模式匹配

1,若第i個開始不等,移動到第i個。直接將sub 0 與src 5 比較。01 2345 678a bcab abca abca bx 2,sub 2 與src 5 比較。01 2345 678a bcab abca abca b 看大神的 理解不了,還是按自己的思路寫乙個,繁瑣還是起碼好理解就行。...

KMP模式匹配

有些演算法,適合從它產生的動機,如何設計與解決問題這樣正向地去介紹。但kmp演算法真的不適合這樣去學。最好的辦法是先搞清楚它所用的資料結構是什麼,再搞清楚怎麼用,最後為什麼的問題就會有恍然大悟的感覺。我試著從這個思路再介紹一下。大家只需要記住一點,pmt是什麼東西。然後自己臨時推這個演算法也是能推出...

KMP模式匹配

屌毛演算法導致我直接罷工兩天沒心情做題。kmp就是用來解決匹配問題,比如字串中找重複子串。核心就是乙個next陣列 含義 next i 即 以i為結尾的非字首字串 和 字首 能夠匹配的最大長度。沒有的話可以為0 abaabaaaa next 5 就是因為aba和 aba 相匹配,為3 如果直接列舉n...