KMP算法
思想:从需要匹配的字符串本身特征出发,比如abcdabce,显然当e不匹配时,完全可以直接后移4位实现abc的匹配。也就是说算法就只需要考虑未成功匹配的字符前面字符串的前缀和后缀的匹配程度即可。
基于此思想,首先需要遍历一下字符串本身,并且在遍历的过程中需要获取当前所访问到的字符以及其前面字符组成的字符串的前缀和后缀匹配情况。其中的遍历过程中产生匹配情况用数组表示。
设待匹配字符串用表示,其长度为
,那么其
数组将由以下方式获取:(利用一个循环即解决了问题!)
# 起始位下标为0def getNext(s):le = len(s)next = np.arange(len(s))i = 0 # 遍历s以及标记后缀j = -1 # 标记前缀next[0] = -1while i < le - 1:if j == -1 or s[i] == s[j]:i += 1j += 1next[i] = jelse:j = next[j]return next
获取数组之后就可以进行字符串匹配了:
def KMP_Index(match_s, matched_s):len0 = len(match_s)len1 = len(matched_s)next = getNext(s)i = -1j = -1while i < len0 and j < len1:if i == -1 or match_s[i] == matched_s[j]:i = i + 1j = j + 1else:i = next[i]if i >= len0:return j - len0else:return 0 # 没有找到
由于之前的数组获取完全没有考虑当前未被正确匹配的字符,所以在很多时候回溯产生移动之后在当前字符上面仍旧无法匹配的情况,对于以上问题进行改进:将未匹配字符考虑其中。
# 起始位下标为0def getNext(s):le = len(s)next = np.arange(len(s))i = 0 # 遍历s以及标记后缀j = -1 # 标记前缀next[0] = -1while i < le - 1:if j == -1 or s[i] == s[j]:i += 1j += 1# 对比回溯值是否相等if s[i] != s[j]:next[i] = j# 由于当前字符回溯之后仍旧和此字符相等,继续回溯else:next[i] = next[j]else:# 继续回溯也可以放到这里,但是这样会造成很大多余的计算量(循环回溯)j = next[j]return next
