KMP中next数组求解思路记录

前言

也是因为自己半个多月前花了一整天还是对KMP算法比较糊涂,然后正好考研复习也要复习到KMP,就再整理一下。
主要解决问题:
为什么需要回溯?
两个序列(i,j)到底在做些什么?

正文

首先上代码:

function getNext (str) {
  let i = 0,
      j = -1,
      next = []
  next[0] = -1
  while (i < str.length) {
    if (j === -1 || str[i] === str[j]) {
      i++
      j++
      next[i] = j
    }
    else {
      j = next[j]
    }
  }
}

JS版,和其他语言基本一致。

首先定义了i和j。
在这一步我认为首先要搞清楚的点是:
1. 当前的next[i]是已知的
2. 我们当前要做的是求解next[i+1]
好像听起来是废话,但是我在理解该算法的时候却是要不停提醒自己这两点,可能真的是我比较菜吧。。。。

那么根据 其他人的博客得到了一个容易理解的思路。

根据第一点:当前next[i]是已知的,我们得到下图:

KMP中next数组求解思路记录_第1张图片
next[i]

即:字符串的[0, j-1]区间的子串与[i - j, i - 1]区间的字串是相等的。
这里先不考虑j-1是否大于等于0,因为代码中其实已经对于j === -1的情况做出了解决,我们不在这里讨论。
就当作已经存在了这两段字串,且它们完全相等
再根据第二点:我们需要做的事求解next[i+1]。
既然[0, j-1]与[i - j, i - 1]相等了,那么:

  1. 只要str[i] === str[j],则两个相同子串的区间长度自然增长了,即[0, j]与[i - j, i]相等,即next[i + 1] = j。
  2. 如果str[i] !== str[j],我们就需要在[0, j-1]中缩减子串的长度找到一个点k,使得[0, k]这个子串与[i - k, i - 1]子串相等:
    KMP中next数组求解思路记录_第2张图片
    image.png

    这张图应该出现在刚刚那篇博客的四个绿圈图之前。
    为什么要找这个K值。其实K值和j - 1的意义是一样的,如果str[k] === str[i]则本来的[0, k]与[i - k, i - 1]相等的两个子串区间自然扩充成了[0, k + 1]和[i - k, i]就求出来next[i+1]了,如果不相等,自然就是继续缩短[0, k]长度,就是使得[0, M]与[i - M, i - 1]相等,不就是一次次递归当前两点嘛。
    怎么找到这个K值。我们第一点说的,当前next[i]是已知的。那么这个K值其实就应该是next[j],根据next[j]的定义,我们知道[0, next[j]]子串和[j - next[j], j - 1]子串相等,那[i -j, i - j + next[j]]子串与[i - next[j], i - 1]子串相等。
    是不是很拗口。。。。
    看图吧还是,看图前请再念一遍:K就是next[j],并再想想next[j]的意义:
    KMP中next数组求解思路记录_第3张图片
    绿圈

    从左至右我们为绿圈编号1,2,3,4
    既然K就是next[j],则 绿圈1和绿圈2相等,因为第一点说的[0, j - 1]与[i - j, i - 1]相等则从i-j往右扩充K个字符的绿圈3必定有一段绿圈4相等,即我们找到了一个K值,满足了绿圈1与绿圈4相等。
    所以我们才会在代码中设置:
else {
    j = next[j]  // 其实是将K值赋值给了j
}

然后就是继续判断str[k]是否等于str[i]啦,如果不等就递归上述操作~~

讲了一堆,自己都觉得讲的不清楚,但是上述几个图真的很棒,感谢原博主。

然后就是再说说j === -1怎么办?
其实也很简单如果j===-1就说明上述寻找K值失败了呗,就是[0, j - 1]中不存在一个K值能够划分出绿圈啊,那么next[i + 1]自然就等于0啦,然后再不断递归不断递归直至求出完整的next数组即可。

结语

我不知道你能不能看懂,因为用文字和图片记录算法实在是比较难,要自己多理解才能知道算法的意思,等我双休日再和你讲讲吧。

你可能感兴趣的:(KMP中next数组求解思路记录)