尚硅谷韩顺平KMP算法兼补充很详尽的KMP算法
引子
在韩顺平灾难性的讲解下我并没有搞懂什么叫KMP算法,在他的推荐下我去看了看博客https://www.cnblogs.com/zzuuoo666/p/9028287.html的内容,确实是有所帮助,将韩顺平没有讲清楚的next数组递归过程点出来了。但是对于我最最关心的next数组如何生生成的细节则说的还是有一些不太清楚。文中虽然举了两个例子,但是这两个例子尤其是第二个例子试图说明“能在前缀中找到字符的例子”时举例还是有一些极端,我在下文补充一个例子。
关于next数组
以下的内容就接着博客https://www.cnblogs.com/zzuuoo666/p/9028287.html的内容去进行叙述。

对于字符串的前缀和后缀我画了上面的图来阐述:
首先,最长的两条绿色线段表示正在成功匹配的前缀和后缀,类比到下面的字符串,就是红线加粗强调的部分。但是继续匹配时,发现F和E无法完成匹配了(正如下面的图),上面图的体现方式是出现了一小段红色的不和谐,我拿紫色的圈圈圈住了这段不和谐。这种情况下,当正在匹配成功的前缀和后缀无法继续匹配成功了。这时候我们需要做什么呢?
这时候就涉及到KMP算法最让人难以理解的部分,即:
k = next[k]
其中该博客原文是这么描述的:
那为何递归前缀索引k = next[k],就能找到长度更短的相同前缀后缀呢?这又归根到next数组的含义。我们拿前缀 p0 pk-1 pk 去跟后缀pj-k pj-1 pj匹配,如果pk 跟pj 失配,下一步就是用p[next[k]] 去跟pj 继续匹配,如果p[ next[k] ]跟pj还是不匹配,则需要寻找长度更短的相同前缀后缀,即下一步用p[ next[ next[k] ] ]去跟pj匹配。此过程相当于模式串的自我匹配,所以不断的递归k = next[k],直到要么找到长度更短的相同前缀后缀,要么没有长度更短的相同前缀后缀。如下图所示...
应该说,这个递归过程博主说得很准确。当我们令k = next[k],k代表的字符立即会由F变为E(即下面图序号2旁边的F变为我拿红圈圈圈住的左边的E)。这时我们会发现一个很神奇的事情,即序号1,2,3指出的ABCD字符串都是相同的,因为序号2等于序号3,序号1等于序号2,可以推知序号1等于序号3(这个等式对应的稳态很重要,因为这个等式指明了为什么不会存在更长相同前缀后缀的原因,因为如果存在更长的,这个等式也成立,则通过k = next[k]同样会找到这个更长的)。这时,如果两个圈住的E是相等的,则一定可以确定我们可以得到一个更小的匹配ABCDE。如果这时候两个圈住的E是不相等的,我们则需要进行进一步的递归,对应到图一,即对序号4指代的蓝线进行进一步的递归,重复上述操作。
韩顺平给出的代码是这样的:
//获取到一个字符串(字串)的部分匹配值
public static int[] kmpNext(String dest) {
//创建一个next数组保存部分匹配值
int[] next = new int[dest.length()];
next[0] = 0; //如果字符串长度为1则部分匹配值就是0
int j = 0;
for (int i = 1; i < dest.length(); i++) {
//当dest.charAt(i) != dest.charAt(j),我们需要从next[j - 1]获取新的j
//直到我们发现有dest.charAt(i) == dest.charAt(j)才退出
while (j > 0 && dest.charAt(i) != dest.charAt(j)) {
j = next[j - 1];
}
//当dest.charAt(i) == dest.charAt(j),部分匹配值就加一
if (dest.charAt(i) == dest.charAt(j)) {
j++;
}
next[i] = j;
}
return next;
}
本文来自博客园,作者:imissinstagram,转载请注明原文链接:https://www.cnblogs.com/LostSecretGarden/p/14770667.html

浙公网安备 33010602011771号