KMP 通俗讲解
本文原计划于
KMP 是 Knuth–Morris–Pratt 算法的简称,由 Knuth、Pratt 和 Morris 在 1977 年共同发布。该算法用来处理字符串匹配的问题(就是给定一个字符串,求它的所有字串中另一个字符串相等的字符串)。
网上有很多讲解 kmp 的文章了,但其实 kmp
有着(自认为)更简单的理解方法。本文记文本串为
首先考虑暴力匹配的过程。
考虑下一个可以和
在 内开始,在 结束。 显然这种情况没什么用。因为这个匹配上的字串的长度甚至连原来匹配上的长度都比不上,最终自然不能匹配上整个 。 在 内开始,一直延伸到 的末尾(其实就是说 为 的后缀)。 这种情况是有可能匹配上整个串的,因为上一次匹配时就匹配到了第六个字符,因此程序处理到这里的时候也不止到后面能不能匹配上。
那么,这种字串有什么特点吗?
注意到,由于这两个字串都匹配上了的某个前缀,因此这两个字串的前 个字符是相等的。又因为 同时也是 的后缀,可以得到 是 的一段相等的前后缀(“Border“)。 在 外, 这种情况显然通过任何已知信息都无法推出了。
由上述分类可以发现,下一个可能匹配上的位置只和已经匹配上的字符串有关。由于这个字符串必然是模式串的某个前缀,我们可以预处理模式串的所有前缀的 Border。但是那么多 Border,选哪一个呢?容易发现,选择最长的 Border 可以保证不漏。因为加入选择较短的 Border,如果较长的 Border 合法,则不能再次回到较长的 Border,因为每次指针都会向后面移动。而如果较短的 Border 合法,必然可以通过上述方法得到。因此我们只需要预处理出模板串的最长 Border 即可。
先假设求出了最长的 Border,我们又该怎么做呢?
首先,令一个指针
- 初始化:
。 - 先暴力匹配。如果
,则 。 - 如果失配了,即
,将 ,然后继续匹配。 - 如果
,直接令 ,相当于暴力匹配时失配的处理方式。 - 如果
,说明找到了一个字串。如果还要寻找更多的字串,可以 ,然后继续匹配。
这个过程的复杂度是,证明可见 OI-Wiki。
我们在暴力匹配是,将
考虑这个东西的实际意义,应该是代表
我们将初始化改成
因此 KMP 的总复杂度就是
代码:网上太多了,因此就不给了。
全文完。
讲个笑话,我自己至今还没写过 KMP 的模板。