KMP模式匹配算法
1.思路分析
参考:https://blog.youkuaiyun.com/lemon_tree12138/article/details/48488813
1.1 知识补充
匹配模式中,每个最优前缀(关于最优前缀可以参考《算法导论》32章内容)S中,S的不为自身的最长的一个等于最优后缀(关于最优后缀可以参考《算法导论》32章内容)的最优前缀SS。这句话可能听起来有一些绕口,下面通过一个实例来说明:
匹配模式P:ababaca
我们选取P的一个最优前缀S = ababa,那么SS = aba.因为SS是S的最优前缀,也是S的最优后缀,而且是最长的。
1.2 例子解释
KMP算法的关键是为我们排除了一些重复匹配,使用主字符串的匹配位置“指针”不需要回溯。这里不妨列举一个小例子。
主字符串T:fababadaaswababaca
匹配模式P:ababaca
假使此时我们正在匹配T的第7位(fababa[d]aaswababaca)和P的第6位(ababa[c]a),而且匹配失败了。针对朴素的匹配模式是T的“指针”回溯到(fa[b]abadaaswababaca),P的“指针”回溯到([a]babaca)。
T“指针”的回溯无疑是浪费了很多的时间。
我们重新检查一下P(ababaca),当我们开始匹配第6位的时候,之前的5位已经匹配完成。而且,[aba]ba= ab[aba]。
那么针对于T而言,fab[aba]daaswababaca这三位是已经匹配过了,匹配的是P(ab[aba]ca),并且[aba]ba= ab[aba]。
可以这样理解,这里选取的最优子串可以代表整个当前匹配字符串T的一部分(ababa),在匹配时,一旦前缀失效,则直接从后缀开始匹配。
这时,我们可以直接将T“指针”直接指向fab[aba]后面一个元素(即i=6,同时,P“指针”指向[aba]后面一个元素(即i=3)
2 代码实现
import time
import numpy as np
class SimpleMatching():
def getIndexOfPinT(self, t, p):
if t is None or p is None:
return None
indexes = []
for i in range(len(t) - len(p) + 1):
for j in range(len(p)):
if t[i + j] == p[j]:
if j == len(p) - 1:
indexes.append(i)
continue
break
return indexes
class KmpMatching():
def getNext(self, text):
if text is None:
return None
lengths = np.zeros(len(text), dtype=int)
for i in range(len(text)):
sub = text[0: i+1]
maxlen = 0
for j in range(len(sub)-1):
subChild = sub[0: j+1]
if sub.endswith(subChild) and len(subChild)>maxlen:
maxlen = len(subChild)
lengths[i] = maxlen
return lengths
def getIndexOfPinT(self, t, p):
if t is None or p is None:
return None
indexes = []
next = self.getNext(p)
indexT = 0
indexP = 0
while indexT < len(t):
if t[indexT] == p[indexP]:
indexP += 1
indexT += 1
elif indexP == 0:
indexT += 1
else:
indexP = next[indexP - 1]
if indexP == len(p):
indexes.append(indexT - indexP)
indexP = 0
return indexes
if __name__ == "__main__":
t = "fababadaaswababaca"*100000
p = "ababaca"
start_SimpleMatch = time.time()
simple_match = SimpleMatching()
indexes1 = simple_match.getIndexOfPinT(t, p)
print(indexes1)
end_SimpleMatch = time.time()
print("The time of SimpleMatching is: ", end_SimpleMatch-start_SimpleMatch)
start_KMPMatch = time.time()
KMP_match = KmpMatching()
indexes2 = KMP_match.getIndexOfPinT(t, p)
print(indexes2)
end_KMPMatch = time.time()
print("The time of KMP-Matching is: ", end_KMPMatch - start_KMPMatch)
>>> The time of SimpleMatching is: 1.203505516052246
The time of KMP-Matching is: 1.116056203842163
从结果可以看出,KMP算法比朴素匹配速度更快