想要完全理解还是有些困难的,这里简单叙述一下思路:
在求next数组时,把“后缀”的p串固定在上方,依次遍历每个元素作为截断的末尾,通过移动下方的p串使其前缀与上方p串的后缀重合,求出其next。
假设已经求出了next[i-1] = j, 考虑next[i]
如果p[j+1] == p[i], 什么也不用做,可得next[i] = j+1
如果p[j+1] != p[i], 需要将下方的p串向后移动,令j = next[j]可以很快地移动到恰当位置,递归下去直到找到j满足p[j+1] = p[i],即可得到next[i] = j+1。
如果直到j = 1都不能满足p[j+1] = p[i],则next[i] = 0
稍加整合可以得到
for (int i = 2, j = 0; i <= n; i++) {
while (j && p[j + 1] != p[i]) j = ne[j];
if (p[j + 1] == p[i]) j++;
ne[i] = j;
}
关于为何next[1]=0:
根据kmp的过程我们可以知道,当j>1时这样做是正确的。
而当p的长度等于1时,每次循环中j一开始始终是0,每次其实就是比较s[i]和p[1]是否相等,从而判断了next[i]为1还是0
至此我们已经求出模式串每一个位置的最大公共前后缀,kmp匹配的过程和上述过程非常相似,只需要注意匹配成功后令j = next[j]可以顺利让匹配继续进行
for (int i = 1, j = 0; i <= m; i++) {
while (j && p[j + 1] != s[i]) j = ne[j];
if (p[j + 1] == s[i]) j++;
if (j == n) {
printf("%d ", i - n + 1);
j = ne[j];
}
}