Kmp算法

2021/08/08

KMP 算法

KMP算法是一种字符串匹配算法,可以在 O(n+m) 的时间复杂度内实现两个字符串的匹配。

它的核心就是 PMT,也就是 next 表。

暴力的匹配算法就是把子串和主串逐位进行匹配,如果不匹配,则子串右移一位。这种情况下必定会产生很多无效匹配,复杂度为 O(mn)。

KMP 说难不难,说简单也不简单,先要理解 PMT。

先解释一下字符串的“前缀”和“后缀”。

  • 如果字符串A和B,存在A=BS,其中S是任意的非空字符串,那就称B为A的前缀。例如,”Harry”的前缀包括{”H”, ”Ha”, ”Har”, ”Harr”},我们把所有前缀组成的集合,称为字符串的前缀集合。
  • 同样可以定义后缀A=SB, 其中S是任意的非空字符串,那就称B为A的后缀,例如,”Potter”的后缀包括{”otter”, ”tter”, ”ter”, ”er”, ”r”},然后把所有后缀组成的集合,称为字符串的后缀集合。要注意的是,字符串本身并不是自己的后缀。

对于一个字符串,它的 PMT 值就是前缀集合和后缀集合中相同最长的字符串的长度。

char a b a b a b c a
index 0 1 2 3 4 5 6 7
value 0 0 1 2 3 4 0 1
  • 对于字符串 ab,前缀集合 {“a”},后缀集合 {“b”},可见部分匹配表的值为0
  • 对于字符串 aba,前缀集合{“a”,”ab”},后缀集合 {“ba”, “a”},可见部分匹配表为1
  • 对于字符串 abab,前缀集合{“a”,”ab”,”aba”},后缀集合{“bab”,”ab”,”b”},最长的相同字符串为 “ab”,所以是2
  • 对于字符串 ababa,前缀集合{“a”,”ab”,”aba”,”abab”},后缀集合{“baba”,”aba”,”ba”,”a”},最长的相同字符串为 “aba”,所以为 3

那么 PMT 表的这个特性有什么好处?好处就是字符匹配过程中,主串上的指针永不后退。

我们一般假定两个指针,一个指针在主串上移动,一个指针在匹配串上移动(如果匹配失败,这个指针回退到指定地方)。

如果在 j 处字符不匹配,那么由于前边所说的模式字符串 PMT 的性质,主字符串中 i 指针之前的 PMT[j −1] 位就一定与模式字符串的第 0 位至第 PMT[j−1] 位是相同的。这是因为主字符串在 i 位失配,也就意味着主字符串从 i−j 到 i 这一段是与模式字符串的 0 到 j 这一段是完全相同的。

这个算法难度大,主要在于难以在脑中可视化,不像是排序算法,树算法,可以直接想象出来。更多的可视化内容可以参考:

Search

    Table of Contents