KMP匹配算法
最近在看程杰的《大话数据结构》一书,看到了第五章,这一章介绍了对串进行匹配的算法,包括朴素模式匹配算法和KMP模式匹配算法。对于KMP算法自己也是搞得有点晕乎了,在这里记录下,以后说不定彻底弄懂了就回来补上。
-
KMP算法是由D.E.Knuth、J.H.Morris和V.R.Pratt三位前辈共同发表的一个模式匹配算法,该算法可以大大避免重复遍历的情况。
-
我们把要查找的字符串称为模式串pattern,相应的在某个字符串中进行查找是否包含模式串的称为文本串text,也称为主串。
-
KMP算法的核心是前缀表,也称为next数组的计算,next数组里面存放的是模式串的最长前后缀的相似度。所谓前后缀是指模式串中的某个子串,而最长前缀是指:该子串第一个字符开始,不包含最后一个字符的串,而最长后缀是指:该子串不包含第一个字符的串。
例子
比如模式串为abcabcd,其前缀分别是:
a
ab
abc
abca
abcab
abcabc
abcabcd
这些前后缀的相同的最长前后缀分别是:
0
0
0
1---->a
2---->ab
3---->abc
0
所以next数组的值是{0, 0, 0, 1, 2, 3, 0} 。
代码实现
-
以上便是自己理解的部分,下面是实现代码:
获取前缀表: void get_prefix_table(const char *pPatternStr, int *next) { if(NULL == pPatternStr) return; int i = 1; int j = 0; int len = strlen(pPatternStr); if(len == 0) return; next[0] = 0; //规定是0 while(i < len) { if(pPatternStr[j] == pPatternStr[i]) { j++; next[i] = j; i++; } else { if(j > 0)//防止数组越界 { j = next[j-1]; //回溯 } else { next[i] = 0; i++; //移动到下一位继续 } } } for(i=len-1; i>0; i--)//next数组往后移动一位,便于KMP算法匹配实现 { next[i] = next[i-1]; } next[0] = -1; } /* KMP算法实现 字符串匹配 main_str: 主串 sub_str: 子串 pos: 从主串main_str的第pos位开始往后查找与子串sub_str相匹配的串 return: 返回子串sub_str相匹配的第一个串的起始位置, 没有匹配则返回-1 */ int kmp_search(const char *main_str, const char *sub_str, int pos) { if(pos<0 || !main_str || !sub_str) return -1; int i = pos; int j = 0; int main_len = strlen(main_str); int sub_len = strlen(sub_str); if(pos>main_len || !sub_len || !main_len) return -1;//越界或者空字符串 int *next = (int *)malloc(sizeof(int) * sub_len); get_prefix_table(sub_str, next); while(i<main_len && j<sub_len) { if(j == sub_len-1 && main_str[i] == sub_str[j]) { free(next); return i-j; } if(main_str[i] == sub_str[j]) { i++; j++; } else { j = next[j]; if(-1 == j) { i++; j++; } } } free(next); return -1; }
-
最后附上一个视频讲解教程:
http://www.bilibili.com/video/av26071816