KMP 算法

时间:2021-09-08 03:17:35

KMP 是一个字符串匹配算法。之所以称之为KMP 是因为这个算法是由Knuth、Morris、Pratt三个提出来的。 这个算法能干什么呢 ? 我想到的有三个: 1. 告诉你一个串是否是另外一个串的子串; 2.一个串在另外一个串里面“重复”了多少次; 3. 可以告诉你一个串的所有前缀子串中是另外一个串的子串的最长前缀是什么。

首先对这三个功能做下说明:

  第1个功能很容易理解,一个串是不是另外一个串的子串,例如 "aba"  是 "bababc" 的子串, 不是 "bacbaa" 的子串;

  

  对于第2功能,我在重复上加了双引号是因为这个重复可以从两个意义上讲。举个例子, 模板串 ”AZAZAZA”, 匹配串 “AZA”。 第一种意义上的重复, 我们可以认为 匹配串 “AZA” 在 模板串 ”AZAZAZA” 中重复了两次:

  ”AZAZAZA”,”AZAZAZA”;

  这种意义上的重复更符合我们通常意义上的重复。

  第二种意义上的重复,我们可以认为匹配串 “AZA” 在 模板串 ”AZAZAZA” 中重复了三次:

  ”AZAZAZA”,”AZAZAZA”,”AZAZAZA”;

  那么KMP 能计算那种意义上的重复次数呢? 幸运的是它都可以做到 ^_^。

  

  第3个功能说的有点绕口(估计表达的有点问题),做下解释,一个字符串所有前缀就是以首字母开头的所有子串的集合。例如 ABA 的前缀包括 A, AB, ABA。KMP 算法能告诉我们匹配串的所有前缀中是模板串子串的最长前缀。照样举个例子:

  匹配串“ABECA",模板串 "ECABEBAC"。

  匹配串的前缀中 ”A", "AB", "ABE",三个子串都是 模板串的子串,但是 ”ABE“是最长的那个前缀串。

  (ps.提到前缀,想起了后缀树,后缀树也可以用来查看一个串是不是另一个串的子串)

现在,我们先从最直观的想法来判断匹配串是不是模板串的子串讲起:

给定模板串 ”cdcdegcdf“, 匹配串 ”cdf“ 如下图:

KMP 算法

第一次迭代,从下标1 开始匹配,第三次失败:

KMP 算法

第二次迭代,从下标2开始匹配,第1次失败:

KMP 算法

第三次迭代,从下标2开始匹配,第3次失败:

KMP 算法

.......

.......

第七次迭代,从下标7开始,匹配成功:

KMP 算法

从上面的迭代过程我们可以发现,在最坏的情况下,我们需要比较 (m*n) 次,才能得出结论。那么有没有更高效的算法呢? 当然,KMP 算法就是为了这个目的设计的。