Next数组是KMP算法的一个难点,而求Next数组时最难的不过就是k = Next[k] 这一行了。首先要理解的是,k是在模式串当前角标之前已经匹配的前缀后缀的长度。
Next[i] 表示在角标i 之前已经匹配的前缀后缀的长度。
如果s[j] == s[k], 说明当前前缀和后缀可以匹配到长度为k的相同字符;
如果s[j] != s[k], 就说明了当前前缀后缀无法匹配到长度为k+1的相同字符,这个时候需要找有没有更短的可能,所以就有了k = Next[k]。
这么说肯定不好理解,还是通过图片来理解。
到最后一位的时候, 发现无法匹配了,也就是找不到K+1了,这时候需要回退一下,看看能不能找到比k长度小的相同字符串,也就执行了k = Next[k];也就是到了下图的位置。
你会发现这里不正好是上上次匹配的地方嘛,也就证明了第六位不能匹配第十位,那就往回退一下,看一下能不能委屈求全一下,找一个短一点的前缀来匹配第十位。
**
优化Next数组
**
接下来进入我们今天的正题,对于下面的图片:
当到达D后不再匹配,按照以前的KMP算法,是变成了下面的图片:
这个时候D又是与B失配,原因很简单,p[Next[4]] = p[1] = b, 而接下来的匹配就是p[1]和D进行匹配,那必然是失配的。我们应该避免出现p[j] = p[Next[j]];,如果出现了,那么下一步必然失配。
可能有人就想到了,那我就再KMP里面修改代码,如果p[Next[j]] = p[j], 那就继续递归寻找。
接下来给出优化Next数组的代码
#include <iostream>
using namespace std;
void GetNext(string s)
{
int len = s.size();
int k = -1, j = 0;
while (j < len)
{
if (k == -1 || s[j] == s[k])
{
k++;
j++;
if (s[j] != s[k])
{
Next[j] = k;
}
else
{
Next[j] = Next[k];
}
}
else
{
k = Next[k];
}
}
}
下面给出Next数组和优化后的Next数组的图片: