字符串匹配算法,查找字符串。Knuth-Morris-Pratt算法
背景
一个字符串为“BBC ABCDAB ABCDABCDABDE”,问里面是否包含另一个字符串“ABCDABD”
算法
逐个搜索,为了提高效率使用《部分匹配表》
部分匹配表
例子一:
字符串: “bread”
**前缀(除最后一个字符所有的头部组合):**b, br. bre, brea
**后缀(除第一个字符所有的尾部组合):**read, ead, ad, d
搜索词 | b | r | e | a | d |
---|---|---|---|---|---|
部分匹配词 | 0 | 0 | 0 | 0 | 0 |
- 字符串“b“的前缀和后缀为空集,共有元素的长度为0
- 字符串”br“的前缀为[b],后缀为[r],共有元素的长度为0
- 字符串”bre“的前缀为[b,br],后缀为[re,r],共有元素的长度为0
- 字符串”brea“的前缀为[b,br,bre],后缀为[rea,re,a],共有元素长度为0
- 字符串”bread“的前缀为[b,br,bre,brea],后缀为[read,rea,re,a],共有元素长度为0
例子二:
字符串:“ABCDABD”
- 字符串”A“的前缀为[],后缀为[],共有元素长度为0
- 字符串”AB“的前缀为[A],后缀为[B],共有元素长度为0
- 字符串”ABC“的前缀为[A,AB],后缀为[BC,C],共有元素长度为0
- 字符串”ABCD“的前缀为[A,AB,ABC],后缀为[BCD,CD,D],共有元素长度为0
- 字符串”ABCDA“的前缀为[A,AB,ABC,ABCD],后缀为[BCDA,CDA,DA,A],共有元素长度为1
- 字符串”ABCDAB“的前缀为[A,AB,ABC,ABCD,ABCDA],后缀为[BCDAB,CDAB,DAB,AB,B],共有元素长度为2
- 字符串”ABCDABD“的前缀为[A,AB,ABC,ABCD,ABCDA,ABCDAB],后缀为[BCDABD,CDABD,DABD,ABD,BD,D],共有元素长度为0
搜索词 | A | B | C | D | A | B | D |
---|---|---|---|---|---|---|---|
部分匹配值 | 0 | 0 | 0 | 0 | 1 | 2 | 0 |
代码实现(Python3)
# -*- coding: utf-8 -*-
# 生成部分匹配表,用字典存储
def generate_dict_match(string_match):
dict_match = {}
for num in range(len(string_match)):
# 字符串组合,‘A’、‘AB‘...’ABCDABD‘
string_flag = string_match[:num + 1]
# 前缀集合
suffix_set = set(string_flag[num:]
for num in range(len(string_flag)) if num != 0)
# 后缀集合
prefix_set = set(string_flag[:num]
for num in range(len(string_flag)) if num != 0)
# 交集元素的总长度
string_lst = list(suffix_set & prefix_set)
dict_match[num] = sum(len(item) for item in string_lst)
return dict_match
# 遍历两个字符串
def find_string(string_origin, string_match):
# 标记是否找到,默认未找到
flag = False
# 中间变量,存储原始字符串查找的起始位置
index_origin = 0
while index_origin < len(string_origin):
index_flag = index_origin
# 计数,如果等于string_match - 1,则表示找到
count = 0
for index_match in range(len(string_match)):
if (string_origin[index_flag] != string_match[index_match]):
break
else:
count += 1
index_flag += 1
# 找到匹配字串,退出循环
if (index_match == len(string_match) - 1):
flag = True
break
if count - dict_match[index_match] != 0:
index_origin += count - dict_match[index_match - 1]
else:
index_origin += 1
if flag == True:
return index_origin - len(string_match) - 1
else:
return 0
if __name__ == "__main__":
string_origin = "BBC ABCDAB ABCDABCDABDE"
string_match = "ABCDABD"
dict_match = generate_dict_match(string_match)
position = find_string(string_origin, string_match)
# 如果找到就输出第一个字母的位置
if position != 0:
print(position)
else:
print('Not Found')