對於兩個字串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...