參考《大話數據結構》 P135
KMP算法用於字符串匹配,kmp算法完成的任務是:給定兩個字符串O和f,長度分別為n和m,判斷f是否在O中出現,如果出現則返回出現的位置。常規方法是遍歷a的每一個位置,然后從該位置開始和b進行匹配,但是這種方法的復雜度是O(nm)。kmp算法通過一個O(m)的預處理,使匹配的復雜度降為O(n+m)
思想:
朴素匹配算法需要兩個指針i,j都遍歷一遍字符串,故復雜度m*n
KMP算法i指針不回溯,j指針的回溯參考next數組,體現了動態規划的思想
原理如下:
藍色表示匹配,紅色為失配
分析藍色部分
如果存在最長公共前后綴的話,比如這樣:
就可以在下次匹配的時候用,這樣避免了i的回溯
實現:
next數組的意義:當模式匹配串T失效的時候,next數組對應的元素知道應該使用T串的哪個元素進行下一輪匹配
1 #include <string> 2 3 void get_next(string T, int *next) 4 { 5 int i = 1; //后綴 6 int j = 0; //前綴 7 next[1] = 0; 8 while (i < T[0]) //T[0]表示字符串長度 9 { 10 if (j == 0 || T[i] == T[j]) 11 { 12 i++; 13 j++; 14 next[i] = j; 15 } 16 else 17 j = next[j]; 18 } 19 } 20 21 int KMP(string S, string T, int pos) 22 { 23 int i = pos; //標記主串S下標 24 int j = 1; //匹配串下標 25 int next[255]; 26 get_next(T, next); 27 while (i <= S[0] && j <= T[0]) //0位置都放字符串長度 28 { 29 if (j == 0 || S[i] == T[j]) 30 { 31 i++; 32 j++; 33 } 34 else 35 j = next[j]; //j退回到合適位置,i不用再回溯了 36 if (j > T[0]) //如果存在j在匹配完最后一個元素后又++了,所以會大於長度 37 return i - T[0]; //i的位置減去匹配串的長度就是匹配串出現的位置 38 else 39 return 0; 40 } 41 }
改進
會出現一種特殊情況:
S = “aaaabcde”
T = "aaaaax"
這樣的話next數組為012345,實際上由於前面都是a,直接調到第一個a就可以了,期望的next數組為000005
這樣next數組構造改為12-15行
1 void get_next(string T, int *next) 2 { 3 int i = 1; //后綴 4 int j = 0; //前綴 5 next[1] = 0; 6 while (i < T[0]) //T[0]表示字符串長度 7 { 8 if (j == 0 || T[i] == T[j]) 9 { 10 i++; 11 j++; 12 if (T[i] != T[j]) 13 next[i] = j; 14 else 15 next[i] = next[j]; 16 } 17 else 18 j = next[j]; 19 } 20 }