kmp算法

参考https://baike.so.com/doc/5460302-5698691.html
kmp算法
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特–莫里斯–普拉特操作(简称KMP算法)。

KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。

时间复杂度O(m+n)。=
输入

正文串T[1,n]和模式串W[1,m]

输出

匹配结果match[1,n]

基本思想
设主串(下文中我们称作T)为:

a b a c a a b a c a b a c a b a a b b

模式串(下文中我们称作W)为:

a b a c a b

用暴力算法匹配字符串过程中,我们会把

T[0] 跟 W[0] 匹配,如果相同则匹配下一个字符,直到出现不相同的情况

,此时我们会丢弃前面的匹配信息,然后把T[1] 跟 W[0]匹配,循环进行,直到主串结束,或者出现匹配成功的情况。这种丢弃前面的匹配信息的方法,极大地降低了匹配效率。

而在KMP算法中,

对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。

比如,在简单的一次匹配失败后,我们会想

将模式串尽量的右移和主串进行匹配。

右移的距离在KMP算法中是如此计算的:在已经匹配的模式串子串中,找出最长的相同的前缀和后缀,然后移动使它们重叠。

在第一次匹配过程中

T: a b a c aa b a c a b a c a b a a b b

W: a b a c a b

在T[5]与W[5]出现了不匹配,而T[0]T[4]是匹配的,现在T[0]T[4]就是上文中说的已经匹配的模式串子串,现在移动找出最长的相同的前缀和后缀并使他们重叠:

T:a b a c aa b a c a b a c a b a a b b

W: a b a c a b

然后在从上次匹配失败的地方进行匹配,这样就减少了匹配次数,增加了效率。

然而,如果每次都要计算最长的相同的前缀反而会浪费时间,所以对于模式串来说,我们会提前计算出每个匹配失败的位置应该移动的距离,花费的时间就成了常数时间。比如:

j 0 1 2 3 4 5
W[j] a b a c a b
F(j) 0 0 1 0 1 2
当W[j]与T[j]不匹配的时候,设置j = F(j-1).

文献中,朱洪对KMP算法作了修改,他修改了KMP算法中的next函数,即求next函数时不但要求W[1,next(j)-1]=W[j-(next(j)-1),j-1],而且要求W[next(j)]<>W[j],他记修改后的next函数为newnext。显然在模式串字符重复高的情况下,朱洪的KMP算法比KMP算法更加有效。

以下给出朱洪的改进KMP算法和next函数和newnext函数的计算算法。

折叠编辑本段串匹配算法
输入:

正文串T[1,n]和模式串W[1,m]

输出: 匹配结果match[1,n]

折叠编辑本段next和newnext
输入: 模式串W[1,m]

输出: next[1,m+1]和newnext[1,m]

朱洪证明了算法1的时间复杂度为O(n),算法2的时间复杂度为O(m)。

下面是更加简洁的算法:

计算过程

假设在执行正文中自位置 i起"返前"的一段与模式的自右至左的匹配检查中,一旦发现不匹配(不管在什么位置),则去执行由W[m]与t[i]+d(x)起始的自右至左的匹配检查,这里x是字符t。它的效果相当于把模式向右滑过d(ti)一段距离。显然,若ti不在模式中出现或仅仅在模式末端出现,则模式向右滑过的最大的一段距离m。图1.1示出了执行BM算法时的各种情况。实线连接发现不匹配以后要进行比较的正文和模式中的字母,虚线连接BM算法在模式向右滑后正文和模式中应对齐的字母,星号表示正文中的一个字母。

图1.1:执行BM算法时的各种情况

BM算法由算法1.3给出,函数d的算法由算法1.4给出。计算函数d的时耗显然是Θ(m)。BM算法的最坏情况时耗是Θ(mn)

编辑本段BM串匹配
输入:

正文串W[1,m]和模式串T[1,n]

输出:

匹配结果match[1,n]

折叠编辑本段d函数
因此有 h(xi+1)=((h(xi)-x·ord(ti))·d+ord(ti+m)mod q ,i=1,2,……,n-m

这里x是一常数,x=dm-1mod q。这就是计算每一长度为m的字符段的散列函数值的递推公式。RK串匹配算法由算法1.5给出。

编辑本段RK串匹配

显然,如果不计执行匹配检查的时间,则RK算法的剩余部分执行时间是Θ(m+n)。不过,如果计及执行匹配检查的时间,则在理论上,RK算法需要时耗Θ(mn)。但是,我们总可设法取q适当大,使得mod函数在计算机中仍可执行而冲突(即不同的字符串具有相同的散列值)又极小可能发生,而使算法的实际执行时间只需Θ(m+n)。

BM算法

BM算法和KMP算法的差别是

对模式串的扫描方式自左至右变成自右至左

另一个差别是

考虑正文中可能出现的字符在模式中的位置

这样做的好处是

当正文中出现模式中没有的字符时就可以将模式大幅度滑过正文。

BM算法的关键是根据给定的模式W[1,m],,定义一个函数d: x->{1,2,…,m},这里x∈∑。函数d给出了正文中可能出现的字符在模式中的位置。

优化思路
KMP算法是可以被进一步优化的。
我们以一个例子来说明。譬如我们给的P字符串是"abcdaabcab",经过KMP算法,应当得到"特征向量"如下表所示:

下标i

0 1 2 3 4 5 6 7 8 9

p(i)

a b c d a a b c a b

next[i]

-1 0 0 0 0 1 1 2 3 1

但是,如果此时发现p(i) == p(k),那么应当将相应的next[i]的值更改为next[k]的值。经过优化后可以得到下面的表格:

下标i

0 1 2 3 4 5 6 7 8 9

p(i)

a b c d a a b c a b

next[i]

-1 0 0 0 0 1 1 2 3 1

优化的next[i]

-1 0 0 0 -1 1 0 0 3 0

(1)next[0]= -1 意义:任何串的第一个字符的模式值规定为-1。

(2)next[j]= -1
意义:模式串T中下标为j的字符,如果与首字符相同,且j的前面的1-k个字符与开头的1-k个字符不等(或者相等但T[k]==T[j])(1≤k<j),如:T=“abCabCad”
则 next[6]=-1,因T[3]=T[6].

(3)next[j]=k 意义:模式串T中下标为j的字符,如果j的前面k个字符与开头的k个字符相等,且T[j] != T[k]
(1≤k<j)即T[0]T[1]T[2]…T[k-1]==T[j-k]T[j-k+1]T[j-k+2]…T[j-1]且T[j]
!= T[k].(1≤k<j);

(4) next[j]=0 意义:除(1)(2)(3)的其他情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值