数据结构 KMP next数组 学习感想

我做这个视频是为了能够帮助一些不懂 kmp 算法的小伙伴们理解,kmp 算法的思想, 以及最关键的 kmp next 数组代码的含义,顺便也帮自己梳理下对 kmp 的理解。
总觉得处处都是巧合,但其实都是必然的。
kmp 算法进行字符串匹配的时候,其复杂度低的一个重要原因是,主串 i 不用回撤。只 要对进行回撤就可以了。
i 不回撤这一特点,我先很朴素的尝试一下啊。 就比如这个例子。
在这里插入图片描述
但我这样想对吗,i 不回撤,不匹配了,j 直接撤回到开头,j=1 。

答案是错。 就如这个例子,这个思路就不行了。
在这里插入图片描述
问题就出现在,不匹配时我 j 就直接退到最开始了。
在这里插入图片描述

这说明什么,说明 i 与 j 不匹配时对 j 的回撤是有要求的。
什么要求?
在这里插入图片描述

如图所示,就是 j 回撤到 k 处时,k 前面的所有 N 个元素要与 i 前面的 N 个元素相应完 全匹配,且 N 要尽量大。这很好理解,只有各自的前 N 个元素完全匹配,才能顺着继续对 i 和 k 处元素进行比较,相等的话就 i 和 j 都加加。而取 N 最大,则可以避免遗漏,而不像 一开始举的第二个例子一样,明明主串中有模式串,可却匹配不到。
有一点我要强调下,N 个元素是指 k 前面的所有元素,必须是,这很好理解,我就不多 说了。
这时候,我们可以发现一个有意思的地方, j 之所以能走到这,这意味着,前面的 N1 个 元素都完全匹配,即 i 前 N 个元素与 j 前 N 个元素完全匹配,而它又与 k 前 N 个元素完全 匹配,则 K 前的所有 N 个元素与 j 前的 N 个元素完全匹配。
在这里插入图片描述

j 是依据什么回撤到 k 处的?观察 kmp 算法代码,j=Next【j】 ,它是依据 next 数组进行 回撤的。而 next 数组代码求的就是模式字符串对应的 next 数组值,如果在 j 处发生不匹配, 而 j=Next【j】=K,那么 j 就回撤到 k 了,那 k 这个数值与什么有关系,k 不就是 j 前面字符 串最大前后缀匹配数加一么?那么我们求 next 数组的话,不就是将目光聚焦于模式字符串 各个元素前的字符串的最大前后缀匹配数的求解上了么?next 数组与主串无关,观察模式 字符串即可。
讲到这我要提一点,本视频,你要能理解我说的啥,你得先知道如何手动的求 next 数 组,你得知道书上给的 next 数组代码,kmp 代码的现象是什么,做法是什么,至于原理我 们慢慢分析,但如果给你代码,让你依据代码的思想填写 next 数组都不会的话,那就不是 理解的问题了,我也爱莫能助。
我们看下面例子:
在这里插入图片描述

我们可以看到,next【15】=7,依据 next 数组代码的思想,我们可以知道,在填写 next 【15】值之前, i=14,j=6,然后判断发现 6 和 14 处都是 b,那么就是相等,加加,填写 j, 为 7。
你看,直接就这么填了,在整个代码中,只有 next【i】=j 这一行代码是在向 next 数组 中录入数据。直接把 j 的值赋给了 next【i】 ,那这么做,要想对,就必须满足一点,j 之前的 所有N个元素都要与i之前的N个元素完全匹配,至于这个 N是否最大?这个不急着解释, 等稍后会过来就发现这必定是最大的了。
那匹配么?j=7,i=15,各自前 6 个元素完全匹配么?从现象上来看,是匹配的,都是 abcaab,可为啥呢?

你看 next 数组代码,if 里头是两个条件,满足其一就可,ch【i】=ch【j】和 j=0
由现象可知,next【9】=1,说明,当 i=8 时,j=0,判断,加加,填写,next【9】=1, 然后再判断(发现都为 a),加加,填写,再判断(都为 b),加加,填写,一直到 i=14,j=6,判 断都为 b,加加,填写,这时,next【15】=7,发现了没有,next 值要到 7,除了一开始进 入 if 时用的条件是 j==0,之后的满足的条件都是 ch【i】==ch【j】,这就强制的使得,当 next 【15】=7 时,j 回撤到 7 处 7 前面的所有 6 个元素与回撤前的前 6 个元素完全匹配,讲到 这还是很好理解的,j 回撤一次,从 15 到 7,完全匹配的现象很好解释。

再看, 对于每一个数,如 next【4】 ,可知前面字符串 abc,匹配数是 0,根据代码推 算,我们推算一下,,,,, 这时,i=3,j=0,我们推算了三四个,可以很容易的发现,当我要 填 next【4】,判断前,i 肯定时 3,进入判断后 i++,对于 4,前面没有匹配数,j=0,依据 这个条件可以进入 if,然后 j++,把 1 填进去,这很符合我们手写时候的思路。
这时我们再来理解,整个代码,只有一行代码,next【i】=j,是在向 next 数组中录入数 据,这就要求,必须所有的 i 所指向的元素,都要在当前情况下进入 if 中,从而填写 i+1 处 的 next 值。进入不了 if,i 不能加加,数据不能录入,那么 next 数组就没法填写完成。
没有匹配数的,next 肯定为 1,依靠 j==0 进入 if,有匹配数的那就依靠 i 与 j 对应元素 相等进入,保证了能够填完 next,所以说 if 一定是两个条件,用或连起来,所有元素都必定 满足其一。

说到这可能会有小伙伴们疑问,没有匹配数时,j 一定为 0 吗?当然了,简单地说,如 果退到一个地方,发现 ch【j】=ch【i】,说明什么,next【i+1】应该填 j+1,j+1 前的所有 j
个元素必定是与 i+1 前的 j 个元素完全对应相等,那么你怎么可以说 i+1 前的字符串没有匹 配数呢?匹配数不就是 j 么?对吧,如果我记得不错,我这招就叫作反证法。那如果一直找 不到 ch【j】=ch【i】,j 就会一直退,退到 0,完美。

好了,next 数组的一大半已经被我们攻克了,还有最后一点,else,j=next【j】,为啥是 next【j】,我们想想啊,如果是 j=0 会怎样,不就是我一开始犯的错误么?直接把回撤到开 头。可为啥 j=next【j】就对了呢?我先说一种解释方法,对于 i=15 时,j=7,很简单,next 【15】=7,那么 j 不就是 7 喽?接下来的步骤就是判断 next【15】和 next【7】,发现不相 等,j=next【j】 ,是一个回撤操作,j 回撤到 3,那就是比较 next【3】与 next【15】,如果相 等,那么 next【16】=4,这要求什么?j 前面的所有两个元素与 i 前面的所有两个元素相等, 看看示例,确实相等,但这是巧合吗?感觉 1,2 和 13,14,怎么会扯上关系呢?他们相等 究竟是不是巧合呢?答案是,不是,这是必然的。为何?
我们看哦, j=7 时,前 6 个为 abcaab, i 前六个为 abcaab,一样,这解释过了,必然的, 可为回撤 j,为啥还是满足 j=3 时,前两个 ab 和 i 前两个 ab 一样?
在这里插入图片描述
为了方便说明,j=7 的为 j1,j=3 的为 j2,i=15.对于 j1 和 i 而言,前六相等,而对于 j2 与 j1 而言,这不就相当于前面的 j1 和 i 吗?j2 前 N 个元素与 j1 前 N 个元素一定相等,但 j1 的前 N 个元素不是又和 i 前 N 个元素完全匹配么?那不就是 j2 前所有元素与 i 前 N 元素完 全匹配么?所以,这不是巧合。
我讲到这,应该有小伙伴 get 到了什么。想想把 1 到 15 当成主串的一部分,而 1 到 7 当成模式串的一部分,对于模式串而言,前 6 个已经和 9 到 14 完全匹配了,接着匹配 7 和 15,发现不等,那么 j 回撤,根据什么回撤,根据它的 next 数组呀。可我们不是正在求完整 的 next 数组么?对呀,但是对于 1 到 7,他们要利用的 next 数组不是已经完整了么?不是 已经可以利用了么?

天衣无缝,你再观察 kmp 代码与 next 数组代码,他们的回撤也都是 j=next【j】,模仿 我刚刚的理解,我们既然可以把一个模式串当成主串和模式串,我们试着把主串和模式串模 仿成一个模式串:
在这里插入图片描述
那在 j 回撤到 k 后匹配,直到 i1 之前,那么都是很契合的,有点那个意思是是吧。当然 这部分没讲很多,意会吧。
所以我们发现这两个代码真的超像。
我之前看书上啊视频上啊啥的,说的递归啥啥啥的,我估摸着可能就,这个味道,但我 真看不下去,一看到递归我就头痛。
现在回头看看 kmp 算法,其 next’数组的求解方式真的是绝了,天才,我刚接触到的时 候,让我编的话那就遍历求前后缀不咯,那复杂度比 BF 算法还要大的很,你再看 kmp 代 码,简简单单几行啊,每看到就忍不住感慨,太强了。
哦,最后一个坑,就是之前提到的,我说要回头解释的,为何 N 完全匹配且一定最大, 为何最大?反证法,如果不是最大,如 next【15】=7,前 6 不是最大,若情况是前 7 最大, 那么反推 next【15】 ,, 其不可能等于 7,而是 8 了,因为其前有 7 个数完全匹配。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值