这个人刚学会 KMP,你们快来嘲讽他。
先设出 $\pi_i$ 表示 $s_{1 \sim n}$ 的最长公共前后缀长度。
现在考虑已经知道了 $\pi_{1 \sim i-1}$,如何求出 $\pi_i$。
如果 $s_{\pi_{i-1}+1} = s_i$,那么本质上就是两个位置各向后推进一个,于是 $\pi_i = \pi_{i-1} + 1$。
那如果不是这样呢?

黑色是已经扫到的所有,红色是 $i-1$ 的答案,蓝色是新扩展的字符,黄色是答案。
可以看到,一开始新扩展的字符并不匹配。我们想要找到黄色。
聪明的你发现,两个黄框都在红框里面。要不然,就把两个红框合并吧?

你发现,黄框正好是红框的 $\pi$!若果还不是,还可以继续进行迭代。
于是,我们只要令初始值为 $\pi_{i-1}$,然后不断往回跳直到跳到 $0$ 或者匹配即可求出 $\pi_i$。
因此,求 $\pi$ 数组的代码其实极其简洁:
|
|
接下来转入正题:KMP。
令 $s$ 为主串,$t$ 为子串。
我们现在得知了整个字符串 $t$ 的 $\pi$。怎样匹配呢?
假如这一位匹配到 $s$ 成功了,我们就可以不理他;
否则,考虑这个情况:

除黑色外颜色相同的部分内容一样。可以看到在红和蓝的地方出现了失配。
此时,直接推倒重来就太亏了。
由于黄色的部分都是一样的,我们为什么不直接把前面的黄色部分拉到这里继续匹配呢?和上面一样,我们也可以继续迭代下去。
这样就可以保证前面始终不降,复杂度线性。
贴一下 P3375 的代码:
:::success[code]
|
|
:::
学会了 ^_^。