BM算法
来源:互联网 发布:js保存数据到本地文件 编辑:程序博客网 时间:2024/06/06 01:47
BM 算法
之前写过关于KMP算法的实现,之后又发现了目前运用于实际的还是BM算法。貌似BM算法比kmp算法快3~5倍的样子。
举例:
说明:
- 我们以前搜索的方式都是从前往后,即头部对齐。但是这样有的时候效率会比较低,BM算法采取了一种聪明的方式,从后往前进行比较。
- 在这里就是”HERE IS”与”EXAMPLE”比较,取最后一位“S”与“E”。不匹配,所以我们继续向后移动。我们还发现,“S”并未出现在”EXAMPLE”之中,所以在这里我们可以直接向后移动7位(搜索词的长度)。
- 同时,这里我们定义一个词坏字符。比如这里的“S”,与“E”(搜索词最后一位)不匹配,所以称“S”为坏字符。
- 下一步,我们比较” A SIMP”。我们同样发现,”P”与”E”不匹配,但是”P”在搜索词(”EXAMPLE”)中出现了。所以不能移动7位,那我们就只能一位一位的移动了吗?答案是否定的,这里出现了一个新的规则——”坏字符规则”。
- 后移位数=坏字符的位置-搜索词中的上一次出现位置。如果”坏字符”不包含在搜索词中,那么”搜索词中的上一次出现位置”定义为-1。这里可以从上一步的”S”得到验证。后移位数
=6−(−1)=7 (从零开始编号)。坏字符的位置是对比于在搜索词的位置。比如这里的”P”,我们需要后移的位数=6−4=2 。 - 所以我们后移了两步,这里比较的是” SIMPLE”与”EXAMPLE”。我们发现”MPLE”与”MPLE”匹配成功,但是再往前就匹配失败了。所以我们这里应该后移的位数
=2−(−1)=3 ,这里的坏字符是”A”。那么这是最好的移动方式吗?答案同样是否定的。 - 在这里,我们定义一个词好后缀 。所有尾部匹配的字符串,比如这里的”MPLE”、”PLE”、”LE”、”E”都是好后缀
- 好后缀规则:后移位数=好后缀的位置-搜索词上一次出现位置。这里好后缀的位置是6(最后一个字符的位置,这里指的是”E”)。同坏字符规则,我们搜索词上一次出现位置应该是-1,但是这里不一样的地方在于,我们好后缀不止一个,所以我们需要逐一比较。比如这里”MPLE”这个好后缀对应的是-1,”PLE”对应的是-1,”LE”对应的是-1,”E”对应的是0。最后我们取0这个值作为搜索词上一次出现位置。所以后移位数
=6−0=6 - 后面移动的方式同上操作,就不赘述了。到最后我们可以发现,其实每次的移动位数就是,取坏字符规则和好后缀规则移动位数大的位数移动。而且,坏字符规则表与好后缀规则表可以事先生成,而不需要原字符串。
步骤:
- 移动位数:7,匹配字符串:”HERE IS”
- 移动位数:2,匹配字符串:” A SIMP”
- 移动位数:6,匹配字符串:”E EXAMP”
- 移动位数:2,匹配字符串:”EXAMPLE”
总结:
- 搜索规则:从后往前搜索
- 编号从0开始
- 坏字符:不匹配的字符
- 坏字符位置:坏字符对比于在搜索词中的位置
- 搜索词的上一次出现位置:坏字符/好后缀在搜索词中上一次出现的位置(从后往前)
- 好后缀:所有尾部匹配的字符串
- 好后缀后移位数需要多次取值。
代码实现(python3)
# -*- coding: utf-8 -*-# 查找搜索词上一次出现的位置def string_index(string, string2): try: str_index = string.index(string[string.index(string2)]) + len(string2) index2 = string[str_index:].index(string2) + len(string2) except: index2 = -1 return index2# 生成好后缀集合def generate_good_suffix_set(string_match): for num in range(len(string_match)): string_flag = string_match[: num + 1] suffix_list = [string_flag[num:] for num in range(len(string_flag))] good_rule = {} for item in suffix_list: good_rule[item] = string_index(string_match, item) return good_rule# 生成坏字符集合def gengerate_bad_char_set(string_match): bad_rule = {} bad_char_lst = [item for item in string_match] for char in bad_char_lst: bad_rule[char] = string_match.index(char) return bad_ruledef find_string(string_origin, string_match): flag = False index_origin = len(string_match) # 好后缀集合 good_rule = generate_good_suffix_set(string_match) bad_rule = gengerate_bad_char_set(string_match) # 从搜索词长度开始搜索,反向匹配 while index_origin <= len(string_origin): index_flag = index_origin # 原字符串的提取 string_origin_temp = string_origin[index_origin - 7:index_origin] # 统计匹配成功的字符串长度 count = 0 # 反向遍历搜索词 for index_match in range(len(string_match)): if (string_origin[index_flag - 1] != string_match[::-1][index_match]): # 坏字符+好后缀字符串 string_temp = string_origin[index_flag - 1:index_flag + count] # 坏字符 char = string_temp[0] # 好后缀字符串 if len(string_temp) > 1: string_temp = string_temp[1:] step1 = len(string_match) - index_match # 坏字符规则 if char in string_match: step1 = step1 - bad_rule[char] - 1 # 好后缀规则 step2 = 1 if len(string_temp) > 1: for j in range(len(string_temp)): step2 = good_rule[string_temp[j:]] if step2 != -1: break # 选出step1与step2更大的值 step = max(step1, step2) break else: # 匹配成功,自加1 count += 1 # 继续匹配 index_flag -= 1 # 如果匹配至最后一个字符,则表示完全匹配成功 if index_match == len(string_match) - 1: flag = True break if flag == True: print(index_origin - len(string_match)) break index_origin += step return flag, index_origin - len(string_match)if __name__ == "__main__": # 测试字符串 string_origin = "HERE IS A SIMPLE EXAMPLE" string_match = "EXAMPLE" string_origin = "BBC ABCDAB ABCDABCDABDE" string_match = "ABCDABD" flag, index = find_string(string_origin, string_match) print(flag, string_origin[index:])
阅读全文
0 0
- BM算法
- BM算法
- BM算法
- BM算法
- BM算法
- BM算法
- BM算法
- BM算法
- BM算法
- BM算法
- BM 算法
- bm算法
- BM算法
- BM算法
- BM算法
- BM算法
- BM算法
- BM算法
- 双列集合
- Bailian4071 查找出现了k次的字符【字符串】
- Android安卓——实现发短信功能的代码
- ndk 编译问题
- set和multiset
- BM算法
- jzoj5484 【清华集训2017模拟11.26】快乐树 (发现性质的dp)
- 详解java的回掉函数与接口
- java 枚举类型
- php trait
- Scrapy框架学习(一)----Scrapy介绍及第一个项目
- IMX6 理解Android编译命令
- (初中OJ)2242. 【2017.11.25普及组模拟】Bob
- 【Scikit-Learn 中文文档】集成方法