更進一步,如果模式串有相同的字元呢,很簡單,思路可以不變,只是失配的時候,我們可以把j向前移x步,而不是移到起始位置即可。失配時移動多少步只和模式有關和目標串無關。我們引進乙個陣列,陣列的值儲存的時模式串對應位置字元的部分匹配值,這個陣列的求法如下:
首先要介紹一下字串的字首和字尾,"字首"指除了最後乙個字元以外,乙個字串的全部頭部組合;"字尾"指除了第乙個字元以外,乙個字串的全部尾部組合。比如字串「abcd」,它的字首為「a」,"ab","abc"。它的字尾為「d」,"cd","bcd"。在kmp演算法中,模式串下標i對應的部分匹配值就是模式串[0,i]字串中字首和字尾最長的共有元素的長度,如模式串為」abcdabd「,則對應的部分匹配值為0000120,即
模式串 : a b c d a b d
部分匹配值 : 0 0 0 0 1 2 0
解釋如下:
"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。
那麼失配時,向前移的部數為:移動位數 = 已匹配的字元數 - 對應的部分匹配值,這樣我們就解決了模式有重複字元的情況了。
以上是kmp演算法的基本思想,即避免回溯。思想並不難理解,難點是kmp在實現的過程中的技巧。
我們先介紹如果求部分匹配值的陣列part_match,我們定義乙個變數max_part_match儲存最大的部分匹配值,初始值為0,並初始化part_match[0] = 0。如果max_part_match等於0且pattern_str[max_part_match] != pattern_str[i](i為迴圈變數,從1開始),說明模式串中還沒出現重複的字元,此時,part_match[i]=max_part_match。如果max_part_match等於0且pattern_str[max_part_match] == pattern_str[i](i為迴圈變數,從1開始),說明開始出現字首和字尾相同的情況,此時++max_part_match,並把part_match[i]=max_part_match。如果max_part_match大於0且pattern_str[max_part_match] == pattern_str[i](i為迴圈變數,從1開始),說明出現字首和字尾相同的長度在增長,此時++max_part_match,並把part_match[i]=max_part_match。如果如果max_part_match大於0且pattern_str[max_part_match] != pattern_str[i](i為迴圈變數,從1開始),
max_part_match = part_match[max_part_match];至於為什麼是這樣,詳細的證明見《演算法導論》,我的理解是part_match[max_part_match]的值為剛好為當前模式串字首和字尾相同的位置的下乙個位置,這些把max_part_match賦值成它的值,就行重新開始了一輪比較,也可以從遞迴的角度理解。
完整**如下(實現在寫部落格之前,變數命名不太一致):
void patternprefixex(std::string pattern_str,std::vector& prefix_offset)
return ;
}
下面的任務就是實現kmp演算法了,先求得部分匹配值陣列,定義乙個變數max_matched表示已經匹配的字元個數,如果等於模式串的長度就返回,思路求部分匹配值相似,需要注意的是,失配時,max_matched值得改變,安裝上面的理論,失配時:移動位數 = 已匹配的字元數 - 對應的部分匹配值,注意這是向前移的位數,max_matched不僅代表已匹配的值還代表模式串的下標,此時上面的公式就程式設計,max_matched(下標) = max_matched(下標) - 移動位數,展開就是:max_matched(下標) = max_matched(下標) - (max_matched(已匹配的數)- 部分匹配值);即max_matched = 部分匹配值,注意這個部分匹配值得位置是max_matched-1的,即max_matched = prefix_offset[max_matched - 1];
完整**如下:
int kmp(std::string text_str,std::string pattern_str)
return -1;
}
KMP 演算法原理解析與 C 實現
kmp 演算法包含兩個部分 利用 pattern 構建 next 陣列 利用 next 資料去匹配長字串 使用 kmp 演算法的好處是,對於被匹配的長字串,我們始終不需要往回走。next 陣列中的每乙個元素,比如說 next i 代表的是在 pattern 中第 i 個位置之前存在的字首和字尾相等的...
KMP演算法c 實現
遞推求解next陣列,初始的情況是next 0 1.假設在某乙個時刻有如下的等式成立 str 0.k 1 str j k.j 1 那麼next j k,在這個前提下,繼續進行下乙個字元的匹配.1 如果str 0.k str j k.j 那麼next j 1 next j 1 k 1.2 反之,如果上...
KMP演算法C實現
這種演算法是d.e.knuth 與v.r.pratt和j.h.morris同時發現的,因此人們稱為kmp演算法。此演算法可以在o n m 的時間數量級上完成串的模式匹配操作。其基本思想是 每當匹配過程中出現字串比較不等時,不需回溯i指標,而是利用已經得到的 部分匹配 結果將模式向右 滑動 盡可能遠的...