字符串模式匹配算法:KMP

在讲KMP之前,我们先看看粗暴的字符串匹配算法。

比方说在原串abacbcabababbcbc中找模式串abababb
在这里插入图片描述
其实就是从原串的第一个元素开始一个个对应比较,但凡发现了不匹配,就回头来从原串中第二个元素开始重复前面比较过程。
在这里插入图片描述

KMP

这种算法看起来很简单,但是效率却不高,于是有人在想,能不能减少比较次数,于是这个被称为改进的模式匹配算法出来了。

为了让大家不放弃,先不列出公式,用图形来概述下。

如果已知下图中原串与模式串中前4位(绿色部分)相同,那么直接可以比较第5个元素。
在这里插入图片描述
再看另一种场景,同样的道理:
在这里插入图片描述
也就是说,只要我们可以确定原串中待比较的元素S前面的元素与模式串中待比较元素P前面的元素相同,则直接比较S与P就可以了。

所以核心问题是:如何跳过已知相同的元素比较。

模式串的分析

下面我们单独来分析模式串。

下图表示模式串中01组成的串与34组成串相同,现在我们要比较第5个元素。
在这里插入图片描述
如果原串与上面的模式串中0到4个元素已经通过比较,只有第5个不一样。
在这里插入图片描述
那么下次比较时我们就可以直接把原串中不通过的元素(红色)直接与模式串中的元素2进行比较,从而跳过了0和1(虚线部分)。
在这里插入图片描述
为什么可以这样?

因为由0到4个元素通过比较可知:
A = P[3], B = P[4]
在这里插入图片描述
而模式串中P[0] = P[3],P[1] = P[4]。
所以A = P[0] , B = P[1] 。

同理,下图中7比较失败后我们再把原串中元素与3比较就可以了。
在这里插入图片描述
综上,我们似乎找到了一个规律:找到模式串中比较失败位置为j,看下它前面有多少字符与模式串最前面的元素相同,比如有k个相同的,那下一趟比较的位置就是k。

如上图中,012与456相同,即3个元素相同,所以下一趟比较的元素就是3,为了最大化地跳过相同元素,k肯定取最大值。

公式

在这里插入图片描述
通过上面的分析,这个公式看起来就能理解了。
先看中间那个:
在这里插入图片描述
绿框框表示对应元素相等,如P[0} = P[j-k],找到最大的k就行了。

当j=0时,表示原串中的元素与模式串中的第0个就不符合,所以直接下一趟了,所以-1可以用来表示走下一趟。
在这里插入图片描述
j为其他情况,即表示待比较的元素不是第0个,且他前面的元素没有相同的,这样只能从头比较了。

靠谱吗?

那么这种直接跳元素的做法,会不会漏?

在这里插入图片描述
比如上图中0-4比较通过,第5个比较失败,按KMP做法,会直接把S[5]与P[2]比较,那么可能会错过以S[1]、S[2]、S[3]、S[4]为起点的比较趟次。

如下图为以S[1]为起点的趟次比较。
在这里插入图片描述
其实不是错过,是没必要。
下面以S1为例给出证明。
由第0趟比较结果可知:
1) S[0] = P[0],S[1] = P[1],S[2] = P[2],S[3] = P[3],S[4] = P[4]。
如果以S[1]为首的趟次也能比较成功的话,则有:
2) S[1] = P[0],S[2] = P[1],S[3] = P[2],S[4] = P[3],S[5] = P[4] ,S[6] = P[5]

结合1)和2)得,S[0] = S[1] = S[2] = … S[5] = P[0] = P[1] = P[5]。
这与第0 趟中S[5]不等于P[5]矛盾。

其中起点的可相应比较。

到此,关于KMP的我们就讲完了,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

章代沫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值