我也學演算法,學演算法
一直以來,對演算法都是理論大於實際,甚至沒有實際.
最近由於項目需要.從新瞭解了一下KMP演算法.唉,討厭這種被動的學習過程.
不過KMP演算法還是很有意思的,用了兩天的時間才總算是弄懂了.期間參考了網上的博文和資料結構.下面分享一下KMP演算法的心得.
KMP的總體思想是利用模式串本身的特性來最佳化匹配的步驟.如何利用自身的特性呢,KMP藉助一個數組來實現,也就是大多數教程中提到的next數組.後面我會介紹next數組是如何構建和使用的.
前面提到KMP演算法需要模式串滿足一定的條件,那麼這個條件是什麼呢.這裡直接引用資料結構書中的等式:
當k < j 時, t1t2…tk-1tk = tj-(k-1)tj-k… tj-1 tj.
如果沒有這個等式,那麼KMP演算法無異於浪費了next大小的空間的最普通的字串匹配演算法.
下面用一張圖來描述一下這個等式在匹配子串的時候起到的作用:
(本圖片假定數組下標從1開始)首先我們找到等式: t1t2 = t6t7.所以當t8不匹配的時候,KMP演算法會自動對齊t1 t2 然後用t3和母串進行匹配.
那麼根據以上的分析,我們可以得出以下的幾個結論(可能得出的有點倉促,同學們還是需要結合課本來進行理解)
1. next數組和模式串的下標是一一對應的.
2. 每個字元對應的next儲存的是其前一個字元匹配的首碼的下標.從上面的圖中可以看到next[8]儲存的是3.
3. 第一個字母沒有首碼,所以它的next儲存的是一個無效的下標.在實際編程中,可以是-1
4. 如果一個字元的前面沒有滿足等式的子串,則其next儲存的是模式串的首字母的下標.
根據我們的結論,下面給出KMP演算法的實現:
void kmp_get_next(char *pattern, int next[]){ int i = 0, j = 0; next[i] = -1; while (i < strlen(pattern) - 1) { if (j == i || pattern[i] == pattern[j]) { if (j != i) { j++; } i++; next[i] = j; } else if (j != 0) { j = next[j]; } else { i++; } }}void kmp_print_next(char *pattern, int next[]){ int i = 0; if (NULL == pattern) { return; } printf("%s 's next array is : \n", pattern); for (i = 0; i < strlen(pattern); i++) { printf("%d\t", next[i]); }}int kmp_is_match(char *pattern, char *basestr, int next[]){ int i = 0; int j = 0; while (i < strlen(basestr)) { if (basestr[i] == basestr[j]) { if (j == strlen(pattern)) { return 0; } i++; j++; } else { j = next[j]; if (j == -1) { i++; } } } return -1;}int kmp_get_match(char *pattern, char *basestr, int next[]){ int i = 0; int j = 0; while (i < strlen(basestr)) { if (basestr[i] == basestr[j]) { if (j == strlen(pattern)) { return i; } i++; j++; } else { j = next[j]; if (j == -1) { i++; } } } return -1;}
#include "kmp.h"int main(int argc, char **argv){ int ret = 0; int i = 0; char *pattern = "abaabc"; int next[10] = {0}; char *basestr = "abcabcabcabccacabdabaabcabceadb"; kmp_get_next(pattern, next); printf("%s matched\n", kmp_is_match(pattern, basestr, next) == 0 ? "is" : "not"); ret = kmp_get_match(pattern, basestr, next); return 0;}
KMP演算法其實理解起來並不難,比較難的就是構造next數組.至少對我而言是這樣.一旦數組構造出來,那剩下的就都好辦了.
可能是我理解的還不夠深把,寫之前覺得有很多內容可以分享,但是開始寫以後發現不知道該寫什麼.希望能夠對同學們有所啟發.