参考《大话数据结构》 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 }