用字符串
![]()
匹配字符串
![]()
,即判断
![]()
是否包含
![]()
,
![]()
表示字符串
![]()
第
![]()
个字符
next数组本身也是一个记录下标的数组,所以会受到我们对于匹配项编码排序的影响,理论上不连续编码也是可以的,甚至不用数字编码都可行,但是为了编程方便,所以我们才采用了连续的整数进行编码
本文采用如下编码方式,这种编码可以避免出现负数,(可以认为0位置的值为和任意字符都可以匹配的值,所以如果当前位置为0,那就需要j和i都要往前移一位)
假如数组next中,第j个元素的值5,则代表当j位置发生不匹配时,要移动
,直到编码为5的元素对准发生不匹配的位置。(这个是整个算法的核心思想)
由于字符串
![]()
每个位置都有可能发生不匹配,所以next的
元素个数刚好等于字符串
的长度。
对于任意字符串
![]()
的头两个元素发生不匹配时,都有固定的值,即0,1(与编码方式有关,按本文的编码方式就是0,1)与之匹配,即对于任意数组next其头两个元素都是0和1。
在计算next数组时,我们是逐步将编码变大的,这里将编码记为
在
![]()
逐步变大的过程中,最长公共前后缀子串的增大也
只能是逐步增大的,但是是可以突然减小的,因为可能在
![]()
逐步增大过程出现一个前面都没出现过的新字符,这时next会突然降为1
结合这特点,我们设计代码的思路就是:
递增,利用
回溯
计算第
![]()
个next值,只需要判断
![]()
的值是否和
![]()
相等
- 相等则
- 不相等:则利用
![]()
往回溯,找到相等的元素或者当
![]()
时直接令
注:
![]()
代表在上一个位置发生不匹配时的最长前缀中的第一个不匹配元素(文字描述有点困难,有机会做个视频解释下)
具体实现代码,主要要考虑的一点就是python中列表的第一个元素下标为0
改进后的KMP
可以看到上面的算法在计算next时其实是不考虑
![]()
的值的,都是考虑从
![]()
到
![]()
的值,这其实是有可能导致重复计算的,因为假如
![]()
,而
![]()
所以应该将0移到该位置,但是上述算法在不考虑
![]()
取值的情况下,将1移到该位置,也就是
![]()
,所以改进算法就是在计算next时考虑当前值
在讲上面KMP算法时,我强调假如数组next中,第j个元素的值5,则代表当j位置发生不匹配时,要移动
,直到编码为5的元素对准发生不匹配的位置。(这个是整个算法的核心思想)
所以和
![]()
比较的永远时当前小标为
![]()
的值即
![]()
,如果
两值相等则该
![]()
的nextval就指向
![]()
的nextval值,如果
不等,则该
![]()
的nextval值就是它自己
![]()
的next的值。
注意:是nextval值指向nextval,因为当
![]()
相等时,代表
![]()
往后(包括j)共
![]()
项,和从首项(包括首项)开始往后的
![]()
项相等,这时就可以借用之前在计算前几项确定的nextval值,直接表现就是,如果
两值相等则该
![]()
的nextval就指向
![]()
的nextval值
确定号next数组之后就是匹配了