KMP算法中关于next数组代码小白式解析

我们先来看看KMP算法中求next数组的代码(下称源码

可能看的一脸懵,没关系,我们一步步来得出它。

我们要先知道next数组的值都是怎么求出来的,

我们先引入一个问题:就是字符串匹配。

如 主串:bbbbbbbbbbbbba

     模式串:bbbba

在主串中找到跟模式串一模一样,常规办法就是暴力算法,如下图。

这种方法不难理解,但太浪费时间(时间复杂度为0(m*n))。如果我们可以以为模式串本身的特点而来跳过一些不必要的匹配过程,那么效率将会大大提高。so我们可以从模式串入手,寻找一种相对便利的方式。

观察模式串为:bbbba 。

       当第一个字符‘b’与主串不匹配时,很简单,模式串重新从第一个开始,主串往下一个走继续判断是否匹配即可,如下图。

      

       可当第三个字符‘b’不匹配时,按照平常,可以继续往下一个走来判断是否匹配。不过我们发现第三个字符‘b’前面的俩个字符是一模一样的,而我们是在第三个字符才发现不匹配的,说明前俩个字符是与主串匹配的,而又因为前两个字符是一样的,那我们就可以让模式串匹配的起点向下一个如下图——这样的话,我们就省下了一次不必要的无效的匹配过程,如下图。

      我们继续往下看,如果是第四个字符‘b'不匹配,说明了前三个字符匹配,那这次我们应该将模式串的起点向下移动多少了?我们看到前两个字符’bb‘(前缀)跟后俩个字符’bb'(后缀)是一样的,那我们就可以将模式串的起点向下移动俩个位置(移动俩次)如下图。

      再来看,如果是最后一个字符‘a’不匹配时,我们发现前缀‘bbb’跟后缀‘bbb’是一样的,那我们就可以将模式串的起点向下移动三个位置(移动三次)如下图。

      看完这个过程,你应该对其原理懂得差不多了。

      现在,我们来总结一下规律:当我们发现在某个位置上的字符不匹配时,我们会观察它前面的字符串的特点,是否有一模一样的(前提:前缀是以第一个字符为开头),这样我们就可以得出下图(如想要关于next数组中前后缀的证明,请点击链接KMP算法中next数组前后缀证明-CSDN博客进行阅读)

我们把next[0]前面加上-1,将上面所得到的数据整体向后移一位,去掉最后一个数,就得到了一次模式串的起点索要移动的次数——这就是我们所要KMP算法中next数组的值(如图)。

       既然原理了解的差不多了,那我们可以看看求next数组的源码(如下代码)

       我知道你想说什么,别急,当开始学的时候,我也看不懂,不过,接下来,我会带你一步一步写出来。

      不管它写的多简单,我们先按照我们刚刚的思路来写:我们思路的核心就是当某个字符不匹配时,找出它前面的字符窜中前缀跟后缀,那这里就涉及到了判断俩字符串是否相等的代码,很简单。

       特别的,对于的二个字符,因为它前面只有一个字符,故当是它不匹配的时候,模式串判断起点仍然不变。由于我们是因为该字符不匹配而得到的下一次模式串的起点索要移动的次数,故当模式串的某个字符不匹配时,下一次模式串的起点索要移动的次数是其前一个我们所算出来的答案,故我们应当要将他们全部向右边依次平移一次,再将最前面的加上-1,就得到了next数组(看代码)。

     A   看到上面的代码,else里面为啥还要判断一次str[j] == str[k]。这是因为当前的字符加上到后缀时,前缀跟后缀不是一样的时候,可能会出现一种情况,就是当前字符与第一个字符是一样的,

这时next[i] = 1(如图)

好,我们既然得到了求next数组的基础代码,现在再将其一步步改成最刚开始看到的源码。

  通过观察,可以发现,sum与k的之间的数学关系——k是等于sum的,那我们便可以用k来代替掉sum(看代码)

再来,观察 j 与 i 之间的数学关系——发现 i = j+1,那我们可以将 i 用 j (让j=2)来代替掉,并且可以将for循环改成while(如图)

再来,在上面我刚刚解释了为啥else 里面还要在判断一次(忘了就看上面A ),观察源码中else中的代码是k = next[k], 

我们来以一个模式串为例:ababa,通过我们上面的源码手动来走一下

可以发现k恒大于next[k]的,又因为next数组是大于等于-1的,则k也大于等于-1。 在源码中,当所比较的前缀跟后缀不一样时,让else中的k=next[k];使得k在不断变小,而源码用的while循环,else中没有j++,从而导致了其循环次数没有增加,在循环次数没有增加的情况下,让k不断地变小,当变成0时,此时发生我们自己所写的代码中else中的判断(即str[k]是否等于str[j - 1]);当k = -1时,就说明了我们自己所写的代码中else中的判断(即str[k]不等于str[j - 1])不成立,那此时next[j - 1]应该等于0(即k+1)。这样我们就推出来了源码!

          如果有错,还请大佬斧正,谢谢! 

  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值