0. 前言
一個字串"BBCABCDABABCDABCDABDE"中是否包含另一個字串"ABCDABD"。 KMP就是最常用的字串匹配演算法之一。KMP有著優秀的時間複雜度O(m+n),網上有很多KMP演算法的解釋,但讀起來都很費勁。直到讀到Jake Boxer的文章,我才真正理解這種演算法。下面我用自己的語言,試圖寫一篇比較好懂的KMP演算法解釋。
1. 演算法過程解釋
首先,字串"BBCABCDAB ABCDABCDABDE"的第一個字元與搜尋字詞"ABCDABD"的第一個字元,進行比較。因為B與A不匹配,所以搜尋字詞後移一位。
就這樣,直到字串有一個字元,與搜尋字詞的第一個字元相同為止。
接著比較字串和搜尋字詞的下一個字元,還是相同。
直到字串有一個字元,與搜尋字詞對應的字元不相同為止。
這時,最自然的反應是,將搜尋字詞整體後移一位,即從上圖B處再從頭逐個比較。這樣做雖然可行,但是效率很差,因為你要把搜尋的初始位置移到已經比較過的位置,重比一遍。
一個基本事實是,當空格與D不匹配時,你其實知道前面六個字元是"ABCDAB"。KMP演算法的想法是,此時不只移動一位,移動數是已經比較的字元數 - 最後一個匹配字元所對應的部分匹配值,這個部分匹配值實質上就是字串頭部和尾部重複部分的最大長度。因此就有了部分匹配值數組:
已知空格與D不匹配時,前面六個字元"ABCDAB"是匹配的。查表可知,最後一個匹配字元B對應的"部分匹配值"為2,因此向後移動的位元為已匹配的字元數減去對應的部分匹配值,即6-2=4。
因為空白格與C不匹配,搜尋字詞還要繼續往後移。這時已匹配的字元數為2("AB"),最後一個匹配字元B對應的"部分匹配值"為0。所以,移動位元為 2,於是將搜尋字詞向後移2位。
因為空白格與A不匹配,繼續後移一位。
逐位比較,直到發現C與D不匹配。於是,移動位元為 6 - 2,繼續將搜尋字詞向後移動4位。
逐位比較,直到搜尋字詞的最後一位,發現完全符合,於是搜尋完成。如果需要找出全部的匹配,移動位元為7 - 0,再將搜尋字詞向後移動7位,剩下的操作就重複了。
2. 部分匹配值數組的產生
首先,要瞭解兩個概念:首碼和尾碼。
"首碼"指除了最後一個字元以外,一個字串的全部頭部組合;
"尾碼"指除了第一個字元以外,一個字串的全部尾部組合。
"部分匹配值"就是"首碼"和"尾碼"的最長的共有元素的長度。以"ABCDABD"為例:
//"A"的首碼和尾碼都為空白集,共有元素的長度為0;//"AB"的首碼為[A],尾碼為[B],共有元素的長度為0;//"ABC"的首碼為[A, AB],尾碼為[BC, C],共有元素的長度0;//"ABCD"的首碼為[A, AB, ABC],尾碼為[BCD, CD, D],共有元素的長度為0;//"ABCDA"的首碼為[A, AB, ABC, ABCD],尾碼為[BCDA, CDA, DA, A],共有元素為"A",長度為1;//"ABCDAB"的首碼為[A, AB, ABC, ABCD, ABCDA],尾碼為[BCDAB, CDAB, DAB, AB, B],共有元素為"AB",長度為2;//"ABCDABD"的首碼為[A, AB, ABC, ABCD, ABCDA, ABCDAB],尾碼為[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的長度為0。
感謝:http://kb.cnblogs.com/page/176818/