KMP-两种方法求next数组

前言

作为408考生,数据结构绕不开KMP算法,网上各种求next数组的方法和结果竟各有不同,本文将带你探讨产生这种局面的原因和求next数组的两种方法。

最佳最快方法

原理

我没去找,反正算的确实是对的。

方法详述

  1. 求模串abaababb的next数组,设置表如下
    在这里插入图片描述

  2. 分别设置next[1]和next[2]的值为0和1
    在这里插入图片描述

  3. 要求i位置next值时,观察前一个位置,即i-1位置。
    (1). 设置一个固定指针指向i-1位置,一个移动指针j首先指向i-1。
    (2). 如果i-1位置的字符和j位置next指向的字符相同,则设置next为j位置next值+1。
    (3). 如果不相同则让j指针移动。移动到目前next指向的位置。直到i-1位置的字符和j位置next指向的字符相同或者j位置next指向0,则设置next为j位置next值+1。

如:这里求序号3的next值,则观察序号2的next值,发现其next值指向序号1。
而序号1对应的字符a与序号2对应的字符不相同,故j继续移动,但此时发现
序号1的next为0,已经不能再往前了。故next取此时j指向的next+1。即0+1=1

在这里插入图片描述

  1. 以下是相同时的情况
    在这里插入图片描述

  2. 后续过程如下图,求取过程为:红绿黄黑
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

分析和思考

上面的案例可能不好理解,不过我们可以得出以下几个结论帮助理解(假设要求第i位置的字符):

  1. 求next值不需要知道第i位置及以后的字符,只需要利用第i位置以前的字符和其对应的next值。
  2. 被比较的字符是i-1位置的字符,它是一直不会变的。比较字符一开始是i-1的next指向的字符,它会因条件不符合(即被比较字符与比较字符不同)而通过next改变。
  3. 最后条件符合(即被比较字符与比较字符相同)时next取决于索引到比较字符的next(即图中的绿色箭头的起点,而不是终点)。

那么第一个字符和第二个字符next值的0和1又从而来呢?
第二字符的next很容易就可以知道:求其next值时只需要考虑第一个字符,因第一个字符前面所以不到其他字符了,所以第二个字符的next值就是第一个字符的next值+1。
那么第一个字符呢?第一个字符前面已经没有字符了,所以我们无法通过这种方法得到第一个字符的next值,而要追根溯源通过原理解释,所以想知道这个问题请看第二种方法。

其他的next数组

在其他人的文章中你或许会看到同一模串会有不同的next数组。比如:对于ababaaababaa对应的next数组可能会有-1,0,0,1,2,3,1,1,2,3,4,5和0,1,1,2,3,4,2,2,3,4,5,6两种形式,但是仔细观察就会发现两个数组每个元素都相差1,那么到底哪个才是正确答案呢?
答案是:两个都有可能!会造成的这样差别的原因是数组教学中的经典问题——数组下标到底是从0还是1开始!第一个是数组从0开始的程序结果,第二个是数组从1开始的手算结果。

如果你已经充分理解了这种方法,请尝试自己编程实现一下吧!

总结

这个方法简而言之就是:在通过前一个字符next串成的链中寻找第一个和前一个字符相同的结点。

最好理解记忆方法

原理

当模串指针移动到j位置时匹配失败了,说明至少主串的指针i前面长度为j-1的子串和模串是相同的。那我们是否可以利用前面这段匹配相同的信息呢?显然是可以的,分析相同前后缀子串就是其中一种方法:如图,当我们发现j指针处不匹配了,需要将j指针向前移动。明显,主串的i-1、i-2、i-3与模串的123位置的字符是一样的。
在这里插入图片描述
那么我们可以直接将j回溯到4位置进行对比。
在这里插入图片描述

但是主串的匹配项不尽相同,而模串的总是一样的,所以我们只用转而求相对主串短很多的模串的相同前后缀就行了。

这个方法源自于KMP的原理,易于理解但是求解过程复杂费时,所以不建议考试用这种方法。如果了解KMP和next原理的同学可以直接跳过这个部分。

前缀字符串和后缀字符串

前缀字符串即除本身外可以构成前缀的串。
后缀字符串即除本身外可以构成后缀的串。

例:对于串 abcde,前缀字符串集={abcd,abc,ab,a},后缀字符串集={bcde,cde,de,e}。

部分匹配值

这是KMP算法早期利用的模串信息,即next数组的前身。

部分匹配值=前缀字符串和后缀字符串最长相同子串的长度。

例:对于串 ababa,前缀字符串集={abab,aba,ab,a},后缀字符串集={baba,aba,ba,a}。最长相同子串为aba。故部分匹配值为3。

next数组意义

next数组又称前缀数组。
数值意义:它代表匹配失败时,模串指针该回溯的位置。
原理意义:它分析该字符前面的字符构成的子串的最长相同前后缀子串。

方法详述

首先我们通过一个简单的例子探查以下部分匹配值的意义,举前文一个简单的例子,对模串ababa求其前缀子串的部分匹配值:
在这里插入图片描述
我们可以发现模串的123位和345位形成了相同的前后缀串,当后3位的aba与abc冲突时,已经匹配过的ab在前缀中也有出现,所以我们不用将模串指针移动到串首,而是相应的检测点。

在这里插入图片描述
这里语言描述比较难以理解,说人话就是:除去冲突的那个字符,你会发现串首和串尾出现了一样的字符序列,所以串首的部分字符可以直接跳过不用比较了。
在这里插入图片描述
从数值上你会发现,部分匹配值表示了,当该字符匹配失败时,模串指针该指向的序号。

转化

了解了部分匹配值的求法和意义后,我们下一步该研究的是如何将部分匹配值转化成next数组。
在这里插入图片描述
如图所示,我们可以直接观察到一个非常有意思的现象(这里注意部分匹配值是的前后缀子串是不包括该串本身的):next[i] = 部分匹配值[i-1]+1 。
我们就足以理解了:next和部分匹配值的意义是一样的。

思考

那么我为什么要说通过这第二种方法求next数组很难呢?

  1. 当模串越长,你通过前后缀求部分匹配值的难度就越大,花的时间就越长
  2. 模串的每一个前缀子串求部分匹配值之间没有关联(拉格朗日插值和牛顿插值的联系),所以无论如何要从头到尾迭代n次

测试链接

我写了一个测试代码挂在自己的网站上,可以获取输入字符串的next和nextval数组,对自己想法有疑惑的兄弟可以测一测。
next数组测试网站

  • 26
    点赞
  • 168
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值