KMP算法流程与实现
KMP算法是字符串查找算法,可以在O(n)的时间复杂度寻找一个字符串内的所有指定子串
下面都按照这个示例进行介绍,在text里寻找pattern,它们的值如下
1 | text = ababcbababaaababcbababaa |
根据pattern生成prefix table
prefix table是由pattern的每个前缀的公共前后缀长度组成,如下图
| 前缀 | 公共前后缀长度 | 解释 |
|---|---|---|
| a | 0 | |
| ab | 0 | |
| aba | 1 | 公共前后缀是a |
| abab | 2 | 公共前后缀是ab |
| ababa(事实上这项不需要) | 3 | 公共前后缀是aba |
我们得到0, 0, 1, 2, 3,然后在前面添加-1,然后去掉最后一个数,得到-1, 0, 0, 1, 2,这个就是prefix table,具体Python代码如下
- 通过观察,我们已经判断了前a个字符“公共前后缀长度”为
m,如果新的字符刚好等于第m+1个字符,则前a+1个字符的“公共前后缀长度”就为m+1。我们直接判断pattern[i] == pattern[prefix_table[i - 1]]即可
1 | def generate_prefix_table(pattern): |
KMP搜索
有了prefix table就可以开始搜索了
先看下如何使用prefix table,先看以下例子,在text里查找pattern
| 变量名 | 值 | 索引变量 | 长度变量 |
|---|---|---|---|
| text | ababcbababaaababcbababaa | i | n |
| pattern | ababa | j | m |
首先依次匹配,直到匹配失败,如下图(i=4,j=4)
1
2
3
4
5i
ababcbababaaababcbababaa
ababa
j
❌根据
prefix_table[j=4]=2,将pattern位置2对应到匹配失败的位置,也就是令j=2,如下图(i=4,j=2)1
2
3
4
5i
ababcbababaaababcbababaa
ababa
j
❌继续匹配,依然失败,如下图(i=4,j=0),此时
prefix_table[j=2]=0,则将位置0移动到匹配失败的位置1
2
3
4
5i
ababcbababaaababcbababaa
ababa
j
❌继续匹配,依然失败,如下图(i=5,j=0),此时
j==0,说明第一个字符都不匹配,则从后面开始匹配1
2
3
4
5i
ababcbababaaababcbababaa
ababa
j
❌当前如下图(i=6,j=0),第一个匹配
1
2
3
4
5i
ababcbababaaababcbababaa
ababa
j
✅此时顺利匹配,当j=m-1时且匹配时,如下图(i=10,j=4),记录当前位置,即pattern开始位置,即
j - i,如果需求只是查找第一个,则可以返回了1
2
3
4
5i
ababcbababaaababcbababaa
ababa
j
✅如果需要继续匹配,则移动至如下位置,此时j=0,i=11
1
2
3
4i
ababcbababaaababcbababaa
ababa
j
代码实现
1 | def kmp_search(text, pattern, prefix_table): |