KMP算法是一个高效的串匹配算法,常被人戏称为‘看毛片算法’,其解决的问题为:
给定两个字符串:
t = 'adjfdajfidjfiasidjfosdsfossssfdsd'
p = 'jfidjf'
判定t中是否包含p
首先,我们来看一个朴素的串匹配算法#coding:utf-8
'''
朴素的串匹配法
'''
def naive_matching(t,p):
m, n = len(p), len(t)
i, j = 0, 0
while i < m and j < n:
if p[i] == t[j]:
i, j = i + 1, j + 1
else: #字符不同,则考虑下一个位置
i, j = 0, j-i+1
if i == m: #i==m表示找到匹配,则返回下标。
return j-i
return -1 #无匹配时,返回特殊值
#coding:utf-8
'''
朴素的串匹配法
'''
def naive_matching(t,p):
m, n = len(p), len(t)
i, j = 0, 0
while i < m and j < n:
if p[i] == t[j]:
i, j = i + 1, j + 1
else: #字符不同,则考虑下一个位置
i, j = 0, j-i+1
if i == m: #i==m表示找到匹配,则返回下标。
return j-i
return -1 #无匹配时,返回特殊值
KMP算法
#coding:utf-8
'''
KMP看毛片算法:
1、朴素的匹配算法,其时间复杂度为O(n*m),KMP的时间复杂度为O(n+m)
2、其基本思想为匹配中不回溯,关键点在于匹配失败时模式串如何前移。
3、核心点在于定义一个长度为m的转移函数pnext,其含义为一个固定的字符串的最长前缀和最长后缀。
4、关于最长前缀和最长后缀:
比如:abcsksabc,那么这个数组的最长前缀和最长后缀相同必然是abc。
cbcbc,最长前缀和最长后缀相同是cbc。
abcbc,最长前缀和最长后缀相同是不存在的。
**注意最长前缀:是说以第一个字符开始,但是不包含最后一个字符。
比如aaaa相同的最长前缀和最长后缀是aaa。**
对于目标字符串ptr,ababaca,长度是7,所以
pnext[0],pnext[1],pnext[2],pnext[3],pnext[4],pnext[5],pnext[6]分别计算的是
a,ab,aba,abab,ababa,ababac,ababaca的相同的最长前缀和最长后缀的长度。
由于a,ab,aba,abab,ababa,ababac,ababaca的相同的最长前缀和最长后缀是
“”,“”,“a”,“ab”,“aba”,“”,“a”,所以next数组的值是[-1,-1,0,1,2,-1,0],
这里-1表示不存在,0表示存在长度为1,2表示存在长度为3。这是为了和代码相对应。
比如pnext中某个字符对应的值是4,则在该字符后的下一个字符不匹配时,可以直接移动往前移动ptr 5个长度,再次进行比较判别
'''
#开始构造pnext表函数
def gen_pnext(p):
i, k, m = 0, -1, len(p)
pnext = [-1]*m #初始数组全为-1
while i< m-1: #生成下一个pnext元素
if k== -1 or p[i] == p[k]:
i, k = i+1, k+1
if p[i] == p[k]:
pnext[i] = pnext[k]
else:
pnext[i] = k
else:
k = pnext[k]
return pnext
def matching_KMP(t, p, pnext):
'''
毛片算法主程序
'''
j, i = 0, 0
n, m = len(t), len(p)
while j < n and i < m:
if i == -1 or t[j] == p[i]:
j, i = j+1, i+1
else:
i = pnext[i]
if i == m:
return j-i
return -1 #无匹配则返回特殊值
程序运行截图