【0代码】大白话说KMP算法

1. next数组 到底是什么意思?

 next数组作为KMP算法里的核心部分,数组里每一项的值决定了在主串即原字符串、被搜索的字符串)匹配失败后,子串即目标字符串)需要重定位的位置。让我们用一个最最简单的例子来诠释如何生成一个next数组。

首先,next数组的取值只和子串有关。

比如有一个子串,值为:“A B C A B C D”。我们直接来生成next数组里,第二个C(后续记为C2)对应索引的值,即next[5]=?。

很简单,只需找出“从子串首字符开始向右”,和“从C2 开始 向左”的两个相同且最长的串即可,next[k]的值即为这两个串其中之一的长度。

在这个例子里,即位于字符串开头的“A B C”,和从C2开始,向左的“A2 B2 C2”。所以next[5]=3,为上述两串之一的长度。

综上,示例里字符串的next数组即为

ABCABCD
0001(A和A22(AB和A2B23(ABC和A2B2C20

2. 为何匹配失败后,需移动子串的指针到next[k]的位置?

举个例子,比如我们正在将主串“...ABCABCY...” 匹配第一点里叙述的子串“ABCABCX”。

当Y匹配到X时,此时匹配失败。但是很容易看出来,X前面的所有字符,和Y前面6位字符肯定是完全相同的,不然也不会走到Y匹配X这一步来。

∵ 由next数组可知X前的C2对应的next[5]=3

∴ X前三个字符A2B2C2和子串开头3个字符ABC一定是相等的

∵ X前面的所有字符,和Y前面6位字符完全相同

∴ Y前三个字符A2B2C2和它们前面3个字符ABC一定是相等的

∴ 下面图片内的四组ABC一定是完全相等的,黄色组的ABC也等于红色组的ABC

image.png

∴ 与其把子串的指针直接回退到0重新开始匹配,不如直接把黄色组和红色组对齐,直接拿Y开始对比A2,节约了对比完全相同的两组ABC的时间,即 移动位数 = 已匹配的字符数 - next[k] = 6 - 3 = 3

image.png

3. 为何匹配失败后,无需移动主串的指针?

用第二点里的主串“...ABCABCY...”来举例子,对于Y,成功的匹配子串的位置相对于它,无非就以下三种情况。

1、成功匹配的子串在Y的前面(不包含Y)

image.png

2、成功匹配的子串包含了Y

image.png

3、成功匹配的子串在Y的后面

image.png

现在,因为在Y处匹配失败了,那么成功匹配的情况就绝对不可能是1和2,那我们还把主串的指针从Y移动到它之前干什么呢?都不用考虑这样到底会不会漏掉前面的某些子串了,即使全漏掉,也不会对最后成功的匹配产生影响,因为既然已经走到Y来了,就说明前面的子串一定没有成功匹配,我们只需要继续向后查找即可。