KMP算法 字符串匹配个人理解
2014-04-09 20:11
197 查看
KMP算法 字符串匹配个人理解
最近很纠结,看了一个多星期,终于基本理解KMP算法,今天写下此文,献给那些依然纠结在KMP中的兄弟同胞们!(如果您已经了解过KMP算法的请跳过这一段)刚刚进来的童鞋可能还不明白什么是KMP算法,现在在这里先大体的说以下,KMP算法其实就是字符串匹配的算法,和C语言的一个函数strstr();功能一样,引用严蔚敏教授的例子,主串 S:acabaabaabcacaabc 模式串 T:abaabcac。用T去匹配S,返回T串在S串中的位置。对于这个问题,很容易想到常规的匹配方法,T串匹配每一个S串的位置都匹配一次(这里就不具体说明,大家一定都很容易想到)。但是这种算法在最坏情况下的时间复杂程度是O(n*m) n、m是两个字符串的串长。接下来我要介绍D.E.Knuth
与 V.R.Pratt 和 J.H.Morris 同时发现的,我们称KMP算法,其时间复杂程度是O(n+m)。
KMP算法和常规算法不同的就是指针回朔的问题,还是用一个例子说明这个问题。
S:a c a b a a b a a b c a c a a b c
T:a b a a b c a c
N:0 1 1 2 2 3 1 2 < -- next[1..8]
第一趟:
指针 i = 1 –> i = 2
S:a c a
b a a b a a b c a c a a b c
T:a b a
a b c a c
指针 j = 1 –> j = 2
j = 2的时候不匹配,next[2] = 1,j指针回朔到1
第二趟:
指针 i = 2
S:a c a
b a a b a a b c a c a a b c
T: a b
a a b c a c
指针 j = 1
j = 1的时候不匹配,next[1] = 0,i、j同时加1
第三趟:
指针 i = 3 –> i = 8
S:a c a b a a b a a
b c a c a a b c
T: a b a a b c a
c
指针 j = 1 –> j = 6
j = 6 时候不匹配,next[6] = 3,j指针回朔到3
第四趟:
指针 i = 8 –> i = 14
S:a c a b a a b a a b c a c a a
b c
T: a b a a b c a c
指针 j = 3 –> j = 9
j走到末尾,匹配完成!
看到这里,相信大多是人都有点糊涂了,没关系,听我慢慢说,我们先分析一个这个匹配过程,从上面看,KMP算法在这个例子上只走了4 趟,如果是常规方法,要6趟才能完成。KMP算法中,还有一个非常非常重要的就是i 指针不回朔,下面将详细讲解是如何实现的。
要做到i指针不回朔,我们还的先对T串进行一下预处理,这里,我们引入一个next函数(计算公式如图1),这个函数值记录的是当匹配出现字符串不相等的时候,i指针不回朔,j指针回朔的位置,也就相当于让T串向右滑动一定的距离后,继续比较。现在对照next值,再重新看一遍是不是稍稍明白了一点了呢?
(图
1)
看到这里,你的脑子里一定在问,为什么next函数可以跳过这么多不需要的匹配过程呢?这个其实就就是KMP的核心所在,我也是困扰在这上面一个星期之久。KMP算法用了一个非常非常简单的原理,比如,已知字符A B C: ( A != B && A == C ) ==> B != C 。很容易理解吧。
接下来我将详细介绍如何利用上面的那条性质进行字符串匹配,还是举上面的例子:
S:a c a b a a b a a
b c a c a a b c
T: a b a a
b c a c
当出现不匹配的时候,我们让 j 指针回朔到第一个a b后面,因为 c 前面 a
b 等于 最前面的 a b,next函数保存的就是满足T[1..n] == T[j-1-n] n最大时 next[j] = n + 1 。
指针回朔后变成这个样子:
S:a c a b a a b a a
b c a c a a b c
T: a
b a a b c a
c
到现在为止,如果你大致明白KMP的原理和比较过程,那么我5个小时的工作还算有些成果。根据上面的过程,我们很容易写出KMP算法的主函数,下面给出C代码(注意C语言字符串下标从0开始):
int Index_KMP(char* s, char* t, int* next) { int i = 0, j = 0; int l1 = strlen(s); int l2 = strlen(t); while (i < l1 && j < l2) { if (next[j] == -1 || s[i] == t[j]) { i++; j++; } else { j = next[j]; } } if (j <= l2) return i - l2; else return -1; } //Index_KMP
上面的代码是在知道next函数的基础上的,我们怎么求next的函数呢?
计算方法如上面图1所示,下面先给出代码:
void get_next(char* t, int* next) { int i = 0, j = -1; // j记录已匹配的个数 int l2 = strlen(t); next[0] = -1; while (i < strlen(t)) { if (j == -1 || t[i] == t[j]) { i++; j++; next[i] = j; } else j = next[j]; } } //get_next
说实话这段代码我也纠结了很久,最后还是被严蔚敏教授一语惊醒梦中人,计算next的值是递推的过程!分析如下:
已知:next[0] = -1;
假设:next[j] = k; 又 T[j] = T[k];
推出:next[j + 1] = k + 1 ;
或者
已知:next[0] = -1;
假设:next[j] = k; 又 T[j] != T[k];
推出:k = next[j] ;
循环上面的过程。
花了6个小时,写到这里,KMP算法基本介绍完毕,下期预告:nextval,KMP算法的改进。
作者: Yes2Me |
可以转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明
网址: http://yes2.me/archives/289
相关文章推荐
- hdu1712
- Suricata之源代码(一)
- 加个精灵瞧一下
- 【Hibernate学习笔记】分层数据验证
- 第一个demo
- Workplan Structure Locked By One User, Can be Unlocked By Another
- java方法的定义格式
- 你没见过的各国历史瞬间
- 什么时候是换工作的最佳时机?
- linux按键驱动编写及测试程序
- 裸板之NAND FLASH
- VMWare10.0在win8.1无法安装
- UI 视图控制器
- error LNK2001: unresolved external symbol _NetUserAdd@16.fatal error LNK1120: 1 unresolved externals
- Ubuntu 13.10安装搜狗输入法
- Resolving FRM-40735 Errors(
- HTTP错误代码完全说明
- HDU_1297Children’s Queue
- SimpleAdapter与自定义Adapter的使用。
- 黑马程序员_oc特有语法一:分类Category和类扩展