c++ kmp算法字符匹配_KMP模式匹配算法

b3753d3cab6132257d43df60c022d870.png

我从对暴力匹配算法的优化角度出发,理解和构造出KMP算法。

模式匹配问题中,有主串,子串,模式串,前缀,后缀,部分匹配值等等概念。教材往往”反着来“,先有解决方案和概念,再强行说”啊,就是这样子的“。但我们都应该清楚,概念是辅助思考,辅助解决遇到的问题的。

假设我们已经弄懂了暴力匹配解决模式匹配的方法,那么也就清楚主串、子串和模式串的概念。

现在,想办法对暴力匹配算法进行优化。所谓”暴力匹配“,就是它穷举遍历了主串的”子串空间“,即每一个子串都去对比一次。但是很多情况,子串部分匹配了模式串,”眼看着就要匹配成功了“,却失配了。针对这些情况,暴力匹配算法都是采取直接复位主串指针,”就好像一切都没发生过“。

同样极端的,我们也可以采取”不后悔“的策略。即下图的”简单策略“,直接跳过那些被部分匹配的字符。

f7db1cf6fddd3e195f32a9c2159c8899.png

但很不幸,这样做存在”错过对的子串“的可能性。

83a1da48ac532314c971cc9c16c431e4.png

这让我们反思:为什么部分匹配失败的字符却组成了匹配成功的子串?

189412349b7f3564d95b2acc58e5adad.png

这个例子帮助我们发现了前面的断言太过于决定,只有那些没有”内在模式“的部分匹配字符才能被跳过。那么那个”内在模式“是什么呢?

d6efac62b8aee0748b079e7ccb913ef1.png

为了理解这种无法简单跳过的情况,我们有了”前缀“和”后缀“等概念。同时也发现,那些部分匹配的情况,我们不需要考虑主串,直接可以用模式串来分析。

91c9a2c3ead6f40e9440d4ad2d829351.png

这些”重复出现的前缀“就是我们要寻找的”内在模式“。因此我们要分析模式串的每一个前缀,同时他们的长度和数组下标一一对应,那么我们只需要分析这些”重复出现的前缀“的长度即可,于是我们定义部分匹配值,为能在前缀里找到的最长的”内在模式“的长度。

可上图说的是跳转?那是因为二者是等价的概念,想不明白就暂停仔细想想(下标、长度)。

于是我们目前优化暴力匹配算法的方案有了,”事前分析“模式串然后制表(next数组),根据这个表跳转模式串指针的位置,保持主串指针不复位。(从而跳过那些无限复位考虑的子串,又不会错过潜在匹配成功的子串)。

可是,如何计算next数组呢?

我们可以人工推算出部分匹配值表(考虑每一个前缀的前缀集合和后缀集合,找到二者交集的最长元素,该长度就是这个前缀的部分匹配值),然后根据等价关系换算到next数组。

但是,如果编码计算呢?毕竟程序没有提供集合运算(或者为了高效,我们也不应该采取集合运算)。没办法了,只能把每种情况,在”内存里看起来是什么样子的“画一画。看有没有思路吧。

7398cf250e66f1cfe44c3864fb4e0bec.png

所以我们可以从最小前缀开始,不断利用并构造next数组!用C++试试看。

#include

最后,还可以对next数组进行优化,因为还存在一种情况,跳转指向的字符与当前字符一样,那么匹配一定会失败从而继续发生next跳转(回溯),不如在构造next数组时就把这种情况考虑进去,从而完成优化。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值