~~ 今天学了KMP算法,课上怎么也想不通,就花了一个晚自习去单步代码,最后终于明白了,下面我来分享一下我的总结,若有什么问题,欢迎各位大佬指正!
我按照我的思路叙述:首先我们来看下这个字符串mum_str = 'abcdabcd'
,规定它有一个这样的列表 mum_list[-1, 0, 0, 0, 0, 1, 2, 3]
这个list 怎么来的呢?首先把这个字符串最后一个字符拿出来提到最前面,变成了mum_str = 'dabcdabc'
,再来一个有len(mum_str)
个元素的列表,第一个元素固定为-1
,从mum_str[1]
开始,在这个例子中从'a'
开始,如果前面有‘a’(不算num_str[0]
显然没有),就在mum_list[1]
的位置写上0
接下来是‘b’,前面没有‘b’吧?没有就在mum_list[2]
的位置写上0。后面的‘c’,‘d’不用解释了吧?
但是
从mum_str[5]
开始就不一样,这个位置的‘a’在前面出现过吗?有就在mum_list[5]
这个位置上写1,
注意!注意!注意!
下一个是问mum_str[6]
的‘b’前面出现过吗?错!
因为之前的mum_list[5]
这个位置上写了1(不为0)。
所以,这时应该问‘ab’在前面出现过吗?
有,就在mum_list[6]
的位置写2;
没有,再问mum_str[6]
的‘b’前面出现过吗?
之后就是问‘abc’在前面出现过吗?
有,就在mum_list[7]
的位置写3。
没有,就…
以此类推。
下面是代码,自己验证一下:
def get_pnext(p):
"""
:param p:mum_str
:return: pnext
"""
i, k, m = 0, -1, len(p)
pnext = [-1] * m
while i < m - 1:
if k == -1 or p[i] == p[k]:
i, k = i + 1, k + 1
pnext[i] = k
else:
k = pnext[k]
return pnext
mum_str = 'abcdabcd'
pnext = get_pnext(mum_str)
print(pnext)
当然这个想法是根据结果的出来的,至少你接受了这个,看一遍母串,就能写出这个pnext。
KMP的难点就在这个pnext上,解决了这个,KMP有手就行!
下面是KMP的完整代码
# KMP算法与朴素匹配不同的是,母串的索引不回退
def matching_KMP(t, p, pnext):
"""
:param t: mum_str
:param p: son_str
:param pnext: get next position
:return: 子串在母串的位置,没找到返回-1
"""
j, i = 0, 0 # j为母串的索引,i为子串的索引
n, m = len(t), len(p)
while j < n and i < m:# i=m说明找到了匹配
if i == -1 or t[j] == p[i]:
j, i = j + 1, i + 1
else:
i = pnext[i] # 这里是关键之处,下面会说明怎么得到pnext[i]
if i == m: # 找到了
return j - i # 因为子串长度为i,所以需要键去i
else:
return -1 # 没找到,返回-1
def get_pnext(p):
"""
:param p:mum_str
:return: pnext
"""
i, k, m = 0, -1, len(p)
pnext = [-1] * m
while i < m - 1:
if k == -1 or p[i] == p[k]:
i, k = i + 1, k + 1
pnext[i] = k
else:
k = pnext[k]
return pnext
if __name__ == '__main__':
mum_str = 'dabcdabcd'
son_str = 'abcd'
pnext = get_pnext(mum_str)
ans = matching_KMP(mum_str, son_str, pnext)
print(ans)
print(pnext)
暂时就先说明到这,没有什么比自己敲一遍代码更有效的掌握!(懂?)
学习算法和数据结构,还任重而道远嘞…