LeetCode之字符串匹配与KMP算法
参考: J.Boxer’s Blog: The Knuth-Morris-Pratt Algorithm in my own words.
算法背景
在计算机科学中,Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)可在一个主文本字符串 S 内查找一个词 W 的出现位置。 此算法通过运用对这个词在不匹配时本身就包含足够的信息来确定下一个匹配将在哪里开始的发现,从而避免重新检查先前匹配的字符。
——from 维基百科
部分匹配表
下面,给出一个模式“abababca”
的部分匹配表:
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 |
如果我有八个字符的模式(在此示例中,假设为“ abababca”
),则我的部分匹配表将包含八个单元格。 如果我查看表格中的第8个单元,则对整个模式(“ abababca”)感兴趣。 如果我查看表格中的第七个单元格,则只对模式中的前七个字符感兴趣(“ abababc”); 第八个(“ a”)无关紧要,不管它来自哪里。 对于第六个,也同样如此。。。
下面介绍==“部分匹配表”==是如何产生的。
首先,要了解两个概念:“前缀"和"后缀”。
“前缀”——指除了最后一个字符以外,一个字符串的全部头部组合;
“后缀”——指除了第一个字符以外,一个字符串的全部尾部组合。
"部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度。以abababca
为例,
- [a] 的前缀和后缀都是空集,共有最长的元素长度为0;
- [ab] 的前缀为[a],后缀为[b],共有最长的元素长度为0;
- [aba] 的前缀为[a, ab],后缀为[ba, a],共有最长的元素长度为0;
- [abab] 的前缀为[a, ab, aba],后缀为[bab, ab, b],共有最长的元素长度为2;
- [ababa]的前缀为[a, ab, aba, abab],后缀为[baba, aba, ba, a,共有最长的元素长度为3;
- [ababab]的前缀为[a, ab, aba, abab, ababa],后缀为[babab, abab, bab, ab, b],共有最长的元素长度为4;
- [abababc]:由于其每个后缀都会包含’c’,而每个前缀都不会包含’c’,所以肯定无共有元素,其长度为0;
- [abababca]的前缀为[a, ab, aba, abab, ababa, ababab, abababc],后缀为[bababca, ababca, babca, abca, bca, ca, a],共有最长的元素长度为1。
如何使用“部分匹配表”
移 动 位 数 = 已 匹 配 的 字 符 数 − 对 应 的 部 分 匹 配 值 移动位数 = 已匹配的字符数 - 对应的部分匹配值 移动位数=已匹配的字符数−对应的部分匹配值
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 |
假设搜索的文本如下:
bacbababaabcbab
|||||
abababca
此时已匹配的[ababa] 长度为5,下一个模式为b
,其对应的value==2,所以接下来移动的距离为
5
−
2
=
3
5-2=3
5−2=3. 如下所示:
// x denotes a skip
bacbababaabcbab
xx|||
abababca