【数据结构与算法】字符串匹配 KMP 算法
BF 算法和 RK 算法
BM 算法和 KMP 算法
Trie 樹和 AC 自動機
KMP 算法
KMP 算法是根據三位作者(D.E.Knuth,J.H.Morris 和 V.R.Pratt)的名字來命名的,算法的全稱是 Knuth Morris Pratt 算法,簡稱為 KMP 算法。
思想
1,KMP算法的核心思想,與BM算法非常相近。假設主冊是a,模式串是b。再模式串與主串匹配的過程中,當遇到不可匹配的字符的時候,找到一些規律,將模式串往后多滑動幾位,跳過肯定不會匹配的情況。
2,當遇到壞字符的時候,我們就要把模式串往后滑動,在滑動的過程中,只要模式串和好前綴有上下重合,前面幾個字符的比較,就相當于拿好前綴的后綴子串,跟模式串的前綴子串在比較。
3,KMP算法就是試圖尋找一種規律:在模式串和主串匹配的過程中,當遇到壞字符后,對于已經對過的好前綴,將模式串一次性滑動很多位?
4,我們只需要拿好前綴本身,在它的后綴子串中,查找最長的那個可以跟好前綴的前綴子串匹配的。假設最長的可匹配的那部分前綴子串是{v},長度是k。我們把模式串一次性往后滑動j-k位,相當于,每次遇到壞字符的時候,我們就把j更新為k,i不變,然后繼續比較。
5,KMP算法也可以提前構建一個數組,用來存儲模式串中每個前綴(這些前綴都有可能是好前綴)的最長可匹配子串的結尾字符下標。將這個數組定義為next數組,很多書中將這個數組起名為**失效函數(failure function)。**next數組的下標是每個前綴結尾字符下標,數組的值是這個前綴的最長可以匹配的前綴子串的結尾字符下標。
next數組(失效函數)計算方法
精髓:k = next[k]
因為前一個的最長串的下一個字符不與最后一個相等,需要找前一個的次長串,問題就變成了求0到next(k)的最長串,如果下個字符與最后一個不等,繼續求次長串,也就是下一個next(k),直到找到,或者完全沒有
①:按照下標從小到大,依次計算next數組的值。當我們要計算next[i]時,前面的next[0],next[1],……,next[i-1]應該已經計算出來了。利用已經計算出來的next值,可以快速推導出next[i]的值。
②:如果next[i-1] = k-1,即子串b[0,k-1]是b[0,i-1]的最長可匹配前綴子串。如果子串b[0,k-1]的下一個字符b[k],與b[0,i-1]的下一個字符b[i]匹配,那子串b[0,k]就是b[0,i]的最長可匹配前綴子串。所以,next[i]等于k。但是,如果b[0,k-1]的下一個字符b[k]跟b[0,i-1]的下一個字符不相等,則需要進一步處理。
③:假設b[0,i]的最長可匹配后綴子串是b[r,i]。如果把最后一個字符去掉,那b[r,i-1]肯定是b[0,i-1]的可匹配后綴子串,但不一定是最長可匹配后綴子串。所以,既然b[0,i-1]最長可匹配后綴子串對應的模式串的前綴子串的下一個字符并不等于b[i],那么我們就可以考察b[0,i-1]的次長可匹配后綴子串b[x,i-1]對應的可匹配前綴子串b[0,i-1-x]的下一個字符b[i-x]是否等于b[i]。如果等于,那[x,i]就是b[0,i]的最長可匹配后綴子串。
④:求b[0,i-1]的次長可匹配后綴子串,次長可匹配后綴子串肯定被包含在最長可匹配后綴子串中,而最長可匹配后綴子串又對應最長可匹配前綴子串b[0,y]。于是,查找b[0,i-1]的次長可匹配后綴子串,這個問題就變成,查找b[0,y]的最長匹配后綴子串的問題。
⑤:按照這個思路,可以考察完所有的b[0,i-1]的可匹配后綴子串b[y,i-1],直到找到一個可匹配的后綴子串,他對應的前綴子串的下一個字符等于b[i],那這個b[y,i]就是b[0,i]的最長可匹配后綴子串。
// a, b分別是主串和模式串;n, m分別是主串和模式串的長度。 public static int kmp(char[] a, int n, char[] b, int m) {int[] next = getNexts(b, m);int j = 0;for (int i = 0; i < n; ++i) {while (j > 0 && a[i] != b[j]) { // 一直找到a[i]和b[j]j = next[j - 1] + 1;}if (a[i] == b[j]) {++j;}if (j == m) { // 找到匹配模式串的了return i - m + 1;}}return -1; }// b表示模式串,m表示模式串的長度 private static int[] getNexts(char[] b, int m) {int[] next = new int[m];next[0] = -1;int k = -1;for (int i = 1; i < m; ++i) {//因為前一個的最長串的下一個字符不與最后一個相等,需要找前一個的次長串,問題就變成了求0到next(k)的最長串,如果下個字符與最后一個不等,繼續求次長串,也就是下一個next(k),直到找到,或者完全沒有while (k != -1 && b[k + 1] != b[i]) {k = next[k];}if (b[k + 1] == b[i]) {++k;}next[i] = k;}return next; }KMP算法復雜度分析
KMP算法包含兩部分,**第一部分是構建next數組,第二部分是借助next數組匹配。**所以時間復雜度分析要分別從這兩部分來分析。
關于第一部分的時間復雜度:
計算next數組得代碼中,第一層for循環中i從1到m-1,即內部的代碼被執行了m-1次,for循環內部代碼有一個while循環,如果我們能知道每次for循環,while循環平均執行的次數,假設是k,那時間復雜度就是O(k*m)。但是,while循環執行的次數不好統計,所以放棄這種方式。
可以找一些參照變量,i和k。i從1開始一直增加到m,而k并不是每次for循環都會增加,所以,k累積增加的值肯定小于m。而while循環里k=next[k]。實際上是在減小k的值,k累積都沒有增加超過m,所以while循環里面k=next[k]總的執行次數也不可能超過m。因此next數組計算的時間復雜度是O(m)。
關于第二部分的時間復雜度
I從0循環增長到n-1,j的增量不可能超過i,所以肯定小于n。而while循環中的那條語句j=next[j-1]+1,不會讓j增長的。因為next[j-1]的值肯定小于j-1,所以while循環中的這條語句實際上也是讓j的值減少。而j總共增長的量都會超過n,那減少的量也不可能超過n,所以while循環中的這條語句總的執行次數也不會超過n,所以這部分的時間復雜度是O(n)。
所以綜合兩部分的時間復雜度,KMP算法的時間復雜度就是O(m+n)。
總結
以上是生活随笔為你收集整理的【数据结构与算法】字符串匹配 KMP 算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java Date Time 教程-时间
- 下一篇: 企业使用RTX腾讯通2013