kmp演算法是一種高效的字串匹配演算法,顯然oi有用,但是網上和書上(aka算導)講的總是看不懂,kz相信是因為kz個人能力不夠,但是通過老師的講解理解後,在這裡嘗試用一種更好理解的方式講解kmp演算法。同時,kz將描述一種個人認為更適合oi的實現方法。
最暴力的字串匹配演算法無非是依次比對文字串t和模式串p,失配的話把p向後移動一格,如下圖。
上述演算法把時間浪費在了反覆前一部分已經匹配了的字元上,而kmp演算法則正是優化了這一點,從而更有效率。
如下圖:
上圖:串a1 == a2 == a'1 == a'2 , b == b'
當匹配上述兩個串時,前面全部匹配,直到c與d失配。若不僅將p向右滑動一格,而是更多:
顯然此時省去了比較a1與a'1的時間,這就是kmp演算法高效的關鍵所在。
kmp通過適當的滑動,使得p的最長字首(a1),與t中 已經匹配成功的串 的最長字尾(a'2)相匹配,因為這一部分已經與p中失配位(d)前面的部分相匹配(a'2 == a2),所以kmp僅通過對p的預處理,即可得出失配時滑動的方案。
預處理時間為o(|p|),匹配時間為o(|t|)。
kmp的原理很好理解,唯一稍有思維難度的是對於滑動方案的計算,這裡使用next的方法來解決。
此方法以及別的實現方法原理相同,僅在細節上有所區別(如陣列下標從哪個開始用),kz認為next方法最便於理解和書寫(aka適合oi)。
next[j]表示當在p[j]失配時,指標j跳轉到next[j],如下圖。
即p[next[j]]以前的串(a1)(p的最長字首),與p[j]以前的串的最長字尾(a2)相等(a1 == a2)。
接下來討論具體的計算方式:
首先明確,next[n+1]由next[n->0]推出。
當p[n] == p[next[n]]時,如下圖:
顯然根據next的定義,存在a1 == a2,此時又有b1 == b2,則a1+b1 == a2+b2,兩個子串相等,所以next[n+1] = next[n]+1。
即p[next[n]+1]以前的串(a1+b2)(p的最長字首),與p[n+1]以前的串的最長字尾(a2+b2)相等(a1+b1 == a2+b2)。
注意,之所以next[n]+1,是為了滿足「以前的串」這個要求。
那麼當p[n] != p[next[n]]時,
誒?存在p[n] != p[next[n]]的情況嗎?是存在的!
因為next的定義,使得相等的串在p[n]與p[next[n]]的前面,所以這兩者不一定相等,況且在「當p[n] == p[next[n]]時」處理的時候,並沒有判斷p[next[n]+1]於p[n+1]是否相等。如果沒看懂,或許可以在看完全文之後回過來看kz到底說了什麼。
總之,當p[n] != p[next[n]]時,如下圖:
a1+b已經無法與a2+d匹配,而a2又不是合法的字尾,所以kz拆開a1,a2來看看:
因為a1 == a2,所以存在e1 == e3,f1 == f2,e2 == e4,
同時由next的定義,存在e1 == e2,所以得到e1 == e4。
如圖:
這樣看起來就和之前的情況相似了,事實上是的,反回去判斷p[next[next[n]]]與p[n]是否相等即可,重複上述過程。
如此往復一直到找到相等的,或者跑到了p[0],那麼此時的next[n+1]就只能是0,可理解為兩個空串相等了。
滑動方案計算:
void kmpnext(char p, int psize, int next)
kmp的重點在於從已匹配部分得到可以優化下一次的資訊,通過減少重複匹配的方式來高效。
如有不當,謝大神指正
// ubwh
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的第乙個字元,依次比較下去,直到得出最後的匹配...