字符串匹配和 KMP 算法
2017-04-27 20:26
344 查看
字符串匹配和 KMP 算法
基本匹配方法
基本的字符串匹配,可通过简单的方式解决:int find(char *s, char *p, int pos) { int i = pos; // 待搜索字符串下标 int j = 0; // 模式当前下标 int slen = strlen(s); // 待搜索字符串长度 int plen = strlen(p); // 模式长度 while (i < slen) { if (s[i] != p[j]) { i = i - j + 1; // 不匹配,回溯 j = 0; } else { i++; // 匹配,下一个位置 j++; if (j == plen) { return i - plen; } } } return -1; }
KMP
基本匹配方法每次不匹配时,都需要从字符串下个位置开始。如果模式中有部分字符串相同,如: abcab ,那么 ab 这个可以不比较,接着从模式的 c 开始,从而减少时间复杂度。KMP 算法对模式字符串共同前后缀进行预处理,先确定模式字符串某个字符串出现不匹配时,应该如何移动模式的下标 j。前后缀是指模式字符串的前后若干个字母,当前后缀相同时,那么只需要简单将指向前缀后一个字母进行比较即可。
当
si == pj时,只需要简单地
i++, j++进行下一次比较,问题在于两者不同时。如下,
s0 s1 .....s(i-1-k).....s(i-1) si.................. sn p0...p(j-1-k).....p(j-1) pj.....pm p0..........p(k-1) pk..pj...pm
当
si != pj,需要确定一个 k,使得有
p0...p(k-1) = s(i-1-k)...s(i-1),这样,就只需要 si 与 pk 进行比较,使得 i 不需要回溯,减少时间复杂度。而确定这个 k 的方法,在 KMP 中称为 next 函数或跳转表,即 k = next[j]。
当 pj 之前的字符串不为空,那么还可以得到
s(i-1-k)...s(i-1) = p(j-1-k)...p(j-1),因此有两个模式子字符串相等:
p0...p(k-1) = p(j-1-k)...p(j-1) 前缀 后缀
因此可见,当 k 取得最大时,得到的加速度最大,而这个 k 与 s 无关,只与模式字符串 p 有关。
当 pj 之前的字符串为空,即 j=0 时,这时相当于 si 和 p0 比较,那么此时的 k 就不能直接取 0 了,会导致 si 再次与 p0 比较而出现死循环,需要进行 i++ ,为了使得行为与匹配时类似,使此时 k = -1 ,那么可进行
i++, j++,与匹配时处理行为相同。
现在,总结一下 kmp 的基本流程:
i = 0, j = 0; while i < len(s): if j = -1 or s[i] == p[j] then i++,j++ if j == len(p): return i - j // 找到了 else j = next[j] return -1
以及对于 next :
if j = 0 then next[j] = -1 else next[j] = MAX({k|k 满足 p0...p(k-1) = p(j-1-k)...p(j-1),1<=k<j)},集合不空) or next[j] = 0 其它情况,即从 p0 开始比较
next 跳转表
next 跳转表的理解要比对 kmp 的理解要麻烦。先按照前面的讨论,
next[0] = -1
从模式匹配的角度来看现在的情况,p 同时成为主串和模式串,对于 next[i],有 p(i-1) == p(next[i-1]),如下
p0..................p(i-1) pi................pn p0..........p(next[i-1]) p(next[i-1]+1)...........pn p0.......p(j-1) p(j).........................pn
若
pi == p[ next[i-1] + 1], :
next[i] = next[i-1] + 1
而不等时,则要找到 k 使得:
p0...p(k-1) = p(i-1-k)...p(i-1)
然后比较 pi 与 pk。看上面 KMP 对于找 k 的过程,这个过程与上面是相同的。
总结上述过程:
next[0] = -1; i = 1, k = 0 while i < len(p): if k == -1 || p[i] == p[k], then i++, k++ next[i] = k else k = next[k]
KMP 总结
汇总以上过程,写成函数为:int* getNext(char *p) { int i, k; int plen = strlen(p); int *next = calloc(plen, sizeof(int)); next[0] = -1; i = 1, k = 0; while (i < plen) { if (k==-1 || p[i] == p[k]) { i++, k++; next[i] = k; } else { k = next[k]; } } return next; } int kmp(char *s, char *p, int pos) { int i = pos; int j = 0; int slen = strlen(s); int plen = strlen(p); int *next = getNext(p); while (i < slen) { if (j == -1 || s[i] == p[j]) { i++, j++; if (j==plen) { free(next); return i-plen; } } else { j = next[j]; printf("%s\n", s); printf("%s\n", p); printf("%d %d\n", i, j); printf("\n"); } } free(next); next = NULL; return -1; }
相关文章推荐
- 字符串匹配算法-KMP
- kmp字符串匹配算法
- KMP字符串匹配算法
- 字符串匹配 KMP 算法
- 字符串匹配算法-kmp
- 字符串匹配 —— KMP 算法
- KMP字符串匹配算法及C语言实现
- 【字符串匹配】——KMP(看毛片算法)——深入讲解next数组的求解
- 算法导论-第32章-字符串匹配:Knuth-Morris-Pratt(KMP)算法C++实现
- KMP 字符串匹配算法
- KMP字符串匹配算法 通俗理解
- ACM 算法 KMP 字符串匹配
- 字符串匹配算法之KMP
- 子字符串substring 问题 - KMP 字符串匹配算法备忘录
- 数据结构与算法之KMP 字符串匹配
- 字符串匹配算法KMP详细解释——深入理解
- KMP(字符串匹配)算法
- 字符串匹配的 KMP 算法
- 字符串匹配算法KMP
- 字符串匹配算法KMP