原理介绍
该算法用于匹配字符串。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串于主串的匹配次数以达到快速匹配的目的。
举个栗子
主串T = ‘abaacababcac’,模式串P=‘ababc’,用模式串P匹配主串T。
KMP算法的核心:计算模式串的前缀表(prefix table)。怎么 计算捏?
步骤1:写出模式串P的所有前缀
步骤2:把每个前缀当成一个独立的串,然后分别找出每个串的最长公共前后缀,并将最长公共前后缀的长度记录下来。
注意:最长公共前后缀一定比原始串要短。
以子串'abab'为例,它的最长公共前后缀为'ab',长度为2,就在该串前记录一个2.
把其他串的最长公共前后缀也找到:
不用管最后一行,给最前面加一个-1,就得到下面这列数字:
这5个数形成的数组就是前缀表。
字符串匹配的过程:
从主串0号索引元素开始匹配:
匹配失败时,将匹配失败元素的前缀表所对应下标的元素拉至当前位置,这就是KMP算法匹配过程的精髓之处。
这里注意前缀表的下标是-1,其实就是把前缀表往右移动一格。
匹配成功!
有时主串中可能会包含好几段模式串。所以还要往后继续匹配:
这时候,已经遍历结束了,并且没有匹配成功,由此可知,只有一段模式串在主串中匹配成功了。
代码实现
def kmp(s, p):
"""
s为主串, p为模式串, 如果s里有p,返回开始下标
"""
next = get_next(p)
i = 0
j = 0 # 分别是s和p的指针
while i < len(s) and j < len(p):
if j == -1 or s[i] == p[j]: # j==-1是由于j=next[j]产生
i += 1
j += 1
else:
j = next[j]
if j == len(p): # j走到了末尾,说明匹配到了
return i - j
else:
return -1
def get_next(p):
"""
p为模式串
返回next数组,即部分匹配表
"""
next = [0] * (len(p) + 1)
next[0] = -1 # 在把PMT进行向右偏移时,第0位的值,我们将其设成了-1,是为了编程的方便
i = 0
j = -1
while i < len(p):
if j == -1 or p[i] == p[j]:
i += 1
j += 1
next[i] = j # 这是最大的不同:记录next[i]
else:
j = next[j]
return next
if __name__ == '__main__':
print(kmp("abaacababcac", "ababc")) # 5
总结
多debug几次,看看具体是怎么循环的,动手的同时多注重理解!
希望身边的小🐏人快快好起来!
我的后遗症是眼睛疼+疯狂打喷嚏?
附一张紧跟时代潮流的手机壳照片