KMP算法的next数组理解总结

KMP算法的next数组理解总结


  KMP算法整体的思路容易理解,主要是采用跳步的思想来避免没有必要的匹配(参考资料1)。在处理的过程中有一个next数组,用来存储相应长度的字符串的最大匹配前缀长度,这个next数组的生成过程在理解上有一些困难,在参考了以下资料之后,觉得需要做一个总结。

  1. 程序员小灰漫画:什么是KMP算法?

  2. KMP算法的next数组通俗解释

  现在基于参考资料2中的例子进行说明,因为觉得更加清晰。next数组的生成主要可以划分为3种情况来考虑:

  1. 当 i 的值为 0 的时候

    这个情况下,next[i] = 0。

  2. 当 s[i] == s[j] 的时候

    这个情况下,主要是采用动态规划的思想,就是当前的next的值可以继承 next[i - 1]的值。如果next[i -1] = 0,那说明这里开始有对称的字符出现了,所以next[i] = 1;如果next[i - 1] = k,就意味着前面的 s[0] ~ s[i - 1] 的对称子串长度是 k,那么当前一个连续的字符相等,说明对称子串的长度在 next[i- 1] 的基础上可以累加1,所以next[i] = next[i - 1] + 1。

  3. 当 s[i] != s[j] 的时候

    这个情况下,就不可以继承 next[i - 1] 的值,因为它已经失去了继承的规律,如下图所示:
    在这里插入图片描述
      字符串的总长是15,当前 i = 14,j = 7( j 指向已匹配最长前缀的下一个位置), s[i] == “t”,s[j] == “a”,因此 s[i] != s[j],这个时候,就显然不是 next[i] = next[i - 1] + 1,也不应该是0,因为它虽然不等于前面的对称子串长度加1,但也有可能跟更小的一个子串对称,比如上图的对称子串就应该是 agct,因此这里就必须回溯进行寻找。

      怎么回溯呢?首先这里要明确一个问题,因为s[i] != s[j],就说明这里的对称子串就断了,所以当我考虑 t 进来的字符串的对称子串时候,肯定不会超过当前的最大对称子串的长度,也就是k,所以 next[i] < k 是必然的,因此在这个条件下,我要回溯的字符串肯定是从 j 下标所在的位置开始的,因为 j == k 是恒成立的,就像当前的这个例子里面一样。

      好啦,到了这里,就可以解决这个问题了,最简单的方法就是暴力遍历嘛,从下标 j 开始,一个个往前比较,当然,KMP算法要是这么麻烦就难免俗套了。肯定还有技巧的嘛,关键就是在这里,难理解的也是这里:

    回溯的过程依然可以用跳步的方式进行,不需要逐步去比较

    3.1 首先,根据第 2 点中的动态规划思想,前面长度为k的连续子串肯定是对称的,就比如例子中,k = 7,因此前面长度为7的子串肯定对称,就是 agctagc了,所以 s[i - k] ~ s[i - 1] 与 s[0] ~ s[j - 1] 是一样的。

    3.2 然后,我想要找出 t 加进来后的最大前缀是什么,那么可以转为满足这样的条件

    • t 前面长度是 n (n < k == j)的子串是对称的,就是说 s[0] ~ s[n] 和 s[i - n] ~ s[i - 1] 是一样的,只有这样,t 加进来后才能得到继续对称的子串(除非只有 t 一个值对称的情况)。

    • s[n + 1] = “t”

    3.3 假设3.2的条件成立,结合3.1 和 3.2 就可以得出这样的结论:

    因为 s[i - k] ~ s[i - 1] == s[0] ~ s[j - 1],所以 s[j- n - 1] ~ s[j - 1] == s[i - n] ~ s[i - 1],(n < k == j)。

    又因为 s[0] ~ s[n] == s[i - n] ~ s[i - 1] ,可得 s[0] ~ s[n] == s[j- n - 1] ~ s[j - 1]

    这就意味着,我想要在下标 j 之前的字符串中找到一个长度为 n 的前缀,让它等于长度为 n 的后缀,并且 s[n + 1] == “t”,那就大功告成了。那next[j] 这个位置存储的数值不刚好是满足这个条件的最大值吗?最大匹配前后缀嘛!那我就不需要再从 j 往回遍历了,直接回溯到最大前缀的下一个位置就可以了,也即是我们常看到的 j = next[j] 的这一行代码了。以这个例子为说明,就是找到了 agctagc 的最大匹配前缀,就是 agc 了,再比较一下s[j] == s[i],就可以让next [14] = 3了。如果 s[j] 又不是 t,就要持续以上过程,直到位置0,没有就是next[14] = 0。如果 s[j] 又不是 t,就要持续以上过程,直到位置0,没有就是next[14] = 0。

  最后有一点想法:KMP算法巧妙是巧妙了,通过跳步和动态规划来减少字符串的匹配次数,但是我就想问个小学生的问题了:它减少比较次数的依据是我的模式串(就是拿来匹配的那个短的字符串)里面有可能重复对称的字符,但是很多英语单词里都不可能出现这种对称重复的模式串啊,那KMP就失效了,所以KMP是用来比较什么的呢?想来想去只有二进制了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值