目录
四、时间复杂度简单匹配算法的时间复杂度为O(m*n),KMP匹配算法时间复杂度为O(m+n).。
一、找出prefix_table
1、把要匹配的字符串pattern拆成子串找出最大公共前后缀,原来的要匹配的字符串列入为求最大公共前后缀的行列里面。最大公共前后缀是指前后两个子串相同,而且子串的长度最大,例如子串ABABCABA的最大公共前后缀是ABA。计算出当前的最大公共前后缀的长度作为prefix_table的元素。前面加上-1,完全不匹配的时候把整个子串移动后一位,就相当于移动到-1的位置。
第3个子串ABAB的最大公共前后缀是AB,下一个子串只要判断下一个元素是不是A即可,假如是A,最大公共前后缀的长度+1,否则最大公共前后缀的长度-1,即此时要对比的对象是A,看看结尾的元素是不是A,是则把元素假如prefixtable。
def get_prefixtable(pattern):
"""
通过传入要和text匹配的子字符串pattern,把pattern拆分成长度为1~len-1子串,
分别找出子串的最大前后缀的长度组成prefix_table
:param pattern:要匹配的子字符串
:return:prefix列表
"""
prefix = [None] * len(pattern)
prefix[0] = 0 #第一个元素只有一个元素,没有前后缀所以为0
length = 0 # 最大前后缀的长度
i = 1 # 从第二个元素开始匹配
while i < len(pattern):
if pattern[i] == pattern[length]:
length += 1
# prefix[i] = length
# i += 1
else:
# 主要是针对开头的元素,一直都匹配不到,
# 当最大前后缀减到1时就会在开头元素不断循环,此时让他等于prefix[length - 1]即可
if length > 0:
length = prefix[length - 1]
# else:
# 记得一定要继续移动i指针
prefix[i] = length
i += 1
return [-1] + prefix
二、KMP算法实现
pattern元素和text元素对相同则继续移动指针,假如不相同则查找prefixtable的值,该值对应的pattern元素是否相等,相等则继续匹配,否则重复上面操作,若是遇到-1的情况,则让i,j指针往后移动。
1、C和A不匹配,把子串整体往后移动,相当于对齐下标为-1的位置。
扫描二维码关注公众号,回复:
9050692 查看本文章
2、指针右移一位,ABAB都匹配上了,但是此时C不匹配,prefixtable显示2,把下标为2的地方对准当前指针位置,继续匹配,
此时匹配到字符串,返回当前指针坐标-子串长度,即可得到匹配到的起始位置。此时最后一位的prefixtable的值为3,把下标为3 的元素和当前指针指向元素对齐,发现要被匹配的子串已经超过了原字符串,所以不可能再会有匹配的位置。
def kmp(pattern, text):
"""
pattern元素和text元素对相同则继续移动指针,假如不相同则查找prefixtable的值,
该值对应的pattern元素是否相等,相等则继续匹配,否则重复上面操作,
若是遇到-1的情况,则让i,j指针往后移动
:param pattern: 要匹配的子串
:param text: 原字符串
:return: 匹配到的下标值的列表
"""
i = 0
j = 0
result = []
text_len = len(text)
pattern_len = len(pattern)
prefix = get_prefixtable(pattern)
if text_len == 0:
return -1
while i < text_len:
if text[i] == pattern[j]:
i += 1
j += 1
else:
j = prefix[j]
# text不匹配的元素和prefix_table的头开始匹配的情况
if j == -1:
i += 1
j += 1
# 假如j == pattern_len - 1则已经匹配到子串最后一个元素,前面的元素都相等,
# 判断假如text[i] == pattern[j]最后一个元素相等则返回开头的下标i-j当前text的位置减去匹配字符串的长度,
# 为匹配的第一个元素位置
if j == pattern_len - 1 and text[i] == pattern[j]:
result.append(i - j)
j = prefix[j]
if len(result) == 0:
return -1
return result
二、测试:
if __name__ == '__main__':
pattern = 'ABABCABAA'
text = 'CABABABCABAABABA'
text1 = 'ABABABCABAABABABCABAAABA'
a = kmp(pattern, text)
print(a)
b = kmp(pattern, text1)
print(b)
print(kmp('ppppppp', text1))
print(kmp('ABABCABAA', ''))
三、结果:
[3] # 说明下标为3第一次匹配
[2, 12] # 说明下标为3第一次匹配 说明下标为12第二次匹配
-1 # 没找到
-1