在用于查找子字符串的算法中,BM(Boyer-Moore)算法是当前有效且应用比较广泛的一种算法,各种文本编辑器的“查找”功能(Ctrl+F),大多采用Boyer-Moore算法。比我们学习的KMP算法快3~5倍。

在1977年,Boyer-Moore算法由德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明

下面通过Java实现BM算法:

package com.buaa;

import java.util.Random;

/**
* @ProjectName StringPatternMatchAlgorithm
* @PackageName com.buaa
* @ClassName BM
* @Description TODO
* @Author 刘吉超
* @Date 2016-05-26 22:26:08
*/
public class BM {
/**
* 利用坏字符规则计算移动位数
*/
public static int badCharacter(String moduleString, char badChar,int badCharSuffix){
return badCharSuffix - moduleString.lastIndexOf(badChar, badCharSuffix);
} /**
* 利用好后缀规则计算移动位数
*/
public static int goodCharacter(String moduleString,int goodCharSuffix){
int result = -1;
// 模式串长度
int moduleLength = moduleString.length();
// 好字符数
int goodCharNum = moduleLength -1 - goodCharSuffix; for(;goodCharNum > 0; goodCharNum--){
String endSection = moduleString.substring(moduleLength - goodCharNum, moduleLength);
String startSection = moduleString.substring(0, goodCharNum);
if(startSection.equals(endSection)){
result = moduleLength - goodCharNum;
}
} return result;
} /**
* BM匹配字符串
*
* @param originString 主串
* @param moduleString 模式串
* @return 若匹配成功,返回下标,否则返回-1
*/
public static int match(String originString, String moduleString){
// 主串
if (originString == null || originString.length() <= 0) {
return -1;
}
// 模式串
if (moduleString == null || moduleString.length() <= 0) {
return -1;
}
// 如果模式串的长度大于主串的长度,那么一定不匹配
if (originString.length() < moduleString.length()) {
return -1;
} int moduleSuffix = moduleString.length() -1;
int module_index = moduleSuffix;
int origin_index = moduleSuffix; for(int ot = origin_index; origin_index < originString.length() && module_index >= 0;){
char oc = originString.charAt(origin_index);
char mc = moduleString.charAt(module_index);
if(oc == mc){
origin_index--;
module_index--;
}else{
// 坏字符规则
int badMove = badCharacter(moduleString,oc,module_index);
// 好字符规则
int goodMove = goodCharacter(moduleString,module_index);
// 下面两句代码可以这样理解,主串位置不动,模式串向右移动
origin_index = ot + Math.max(badMove, goodMove);
module_index = moduleSuffix;
// ot就是中间变量
ot = origin_index;
}
} if(module_index < 0){
// 多减了一次
return origin_index + 1;
} return -1;
} /**
* 随机生成字符串
*
* @param length 表示生成字符串的长度
* @return String
*/
public static String generateString(int length) {
String baseString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; StringBuilder result = new StringBuilder(); Random random = new Random();
for (int i = 0; i < length; i++) {
result.append(baseString.charAt(random.nextInt(baseString.length())));
} return result.toString();
} public static void main(String[] args) {
// 主串
// String originString = generateString(10);
String originString = "HERE IS A SIMPLE EXAMPLE";
// 模式串
// String moduleString = generateString(4);
String moduleString = "EXAMPLE";
// 坏字符规则表
// int[] badCharacterArray = badCharacter(originString,moduleString); System.out.println("主串:" + originString);
System.out.println("模式串:" + moduleString); int index = match(originString, moduleString);
System.out.println("匹配的下标:" + index);
}
}

下面,我来解释上面代码

首先先明确两个规则:坏字符规则、好后缀规则

1、坏字符规则

后移位数 = 坏字符的位置 - 模式串中的坏字符上一次出现位置

如果"坏字符"不包含在模式串之中,则上一次出现位置为 -1。以下面这两个字符串为例

因为"P"与"E"不匹配,所以"P"被称为"坏字符",它出现在模式串(模式串就是EXAMPLE)的第6位(从0开始编号),在模式串中的上一次出现位置为4,所以后移 6 - 4 = 2位

2、好后缀规则

后移位数 = 好后缀的位置 - 模式串中的上一次出现位置

举例来说,如果模式串"ABCDAB"的后一个"AB"是"好后缀"。那么它的位置是5(从0开始计算,取最后的"B"的值),在模式串中的上一次出现位置是1(第一个"B"的位置),所以后移 5 - 1 = 4位,前一个"AB"移到后一个"AB"的位置。

再举一个例子,如果模式串"ABCDEF"的"EF"是好后缀,则"EF"的位置是5 ,上一次出现的位置是 -1(即未出现),所以后移 5 - (-1) = 6位,即整个字符串移到"F"的后一位。

这个规则有三个注意点:

(1)"好后缀"的位置以最后一个字符为准。假定"ABCDEF"的"EF"是好后缀,则它的位置以"F"为准,即5(从0开始计算)。

      (2)如果"好后缀"在模式串中只出现一次,则它的上一次出现位置为 -1。比如,"EF"在"ABCDEF"之中只出现一次,则它的上一次出现位置为-1(即未出现)。

(3)如果"好后缀"有多个,这时应该选择最长的那个"好后缀"且它的上一次出现位置必须在头部。比如,假定"BABCDAB"的"好后缀"是"DAB"、"AB"、"B",这时"好后缀"的上一次出现位置是什么?回答是,此时采用的好后缀是"B",它的上一次出现位置是头部,即第0位,其他好后缀上一次出现的位置都不在头部

规则讲完啦,接下说一下上面代码

1、假定主串为"HERE IS A SIMPLE EXAMPLE",模式串为"EXAMPLE",模式串也就是搜索词

主串

HERE IS A SIMPLE EXAMPLE

模式串

EXAMPLE

2、首先,主串与模式串头部对齐,从尾部开始比较。这是一个很聪明的想法,因为如果尾部字符不匹配,那么只要一次比较,就可以知道前7个字符(整体上)肯定不是要找的结果。我们看到,"S"与"E"不匹配。这时,"S"就被称为"坏字符"(bad character),这时用坏字符规则得到的是7,用好后缀规则得到的是-1,选择大的作为后移位数,这里选择7

3、依然从尾部开始比较,发现"P"与"E"不匹配,所以"P"是"坏字符"。

4、这时用坏字符规则得到的是2,用好后缀规则得到的是-1,选择大的作为后移位数,这里选择2

5、依然从尾部开始比较,"E"与"E"匹配。

6、比较前面一位,"LE"与"LE"匹配。

7、比较前面一位,"PLE"与"PLE"匹配

8、比较前面一位,"MPLE"与"MPLE"匹配。我们把这种情况称为"好后缀"(good suffix),即所有尾部匹配的字符串。注意,"MPLE"、"PLE"、"LE"、"E"都是好后缀

9、比较前一位,发现"I"与"A"不匹配。所以,"I"是"坏字符",这时用坏字符规则得到的是3,用好后缀规则得到的是6,选择大的作为后移位数,这里选择6

10、继续从尾部开始比较,"P"与"E"不匹配,因此"P"是"坏字符"。这时用坏字符规则得到的是2,用好后缀规则得到的是-1,选择大的作为后移位数,这里选择2

11. 从尾部开始逐位比较,发现全部匹配,于是搜索结束

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【刘超★ljc】。

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

字符串匹配算法-BM的更多相关文章

  1. [转] 字符串模式匹配算法——BM、Horspool、Sunday、KMP、KR、AC算法一网打尽

    字符串模式匹配算法——BM.Horspool.Sunday.KMP.KR.AC算法一网打尽 转载自:http://dsqiu.iteye.com/blog/1700312 本文内容框架: §1 Boy ...

  2. 字符串模式匹配算法——BM、Horspool、Sunday、KMP、KR、AC算法一网打尽

    字符串模式匹配算法——BM.Horspool.Sunday.KMP.KR.AC算法一网打尽 本文内容框架: §1 Boyer-Moore算法 §2 Horspool算法 §3 Sunday算法 §4 ...

  3. BM和KMP字符串匹配算法学习

    BM和KMP字符串匹配算法学习 分类: 研究与学习 字符串匹配BM(Boyer-Moore)算法学习心得 http://www.cnblogs.com/a180285/archive/2011/12/ ...

  4. 字符串匹配算法之BM算法

    BM算法,全称是Boyer-Moore算法,1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了一种新的字符串匹配算法. BM算法定义了两个规则: ...

  5. 字符串模式匹配算法——BM、Horspool、Sunday、KMP、KR、AC算法

    ref : https://dsqiu.iteye.com/blog/1700312 本文内容框架: §1 Boyer-Moore算法 §2 Horspool算法 §3 Sunday算法 §4 KMP ...

  6. 字符串匹配算法(二)-BM算法详解

    我们在字符串匹配算法(一)学习了BF算法和RK算法,那有没更加高效的字符串匹配算法呢.我们今天就来聊一聊BM算法. BM算法 我们把模式串和主串的匹配过程,可以看做是固定主串,然后模式串不断在往后滑动 ...

  7. 图解BM(Boyer-Moore)字符串匹配算法+代码实现

    简介 本篇文章主要分为两个大的部分,第一部分通过图解的方式讲解BM算法,第二部分则代码实现一个简易的BM算法. 基本概念 bm是一个字符串匹配算法,有实验统计,该算法是著名kmp算法性能的3-4倍,其 ...

  8. Boyer-Moore 字符串匹配算法

    字符串匹配问题的形式定义: 文本(Text)是一个长度为 n 的数组 T[1..n]: 模式(Pattern)是一个长度为 m 且 m≤n 的数组 P[1..m]: T 和 P 中的元素都属于有限的字 ...

  9. KMP单模快速字符串匹配算法

    KMP算法是由Knuth,Morris,Pratt共同提出的算法,专门用来解决模式串的匹配,无论目标序列和模式串是什么样子的,都可以在线性时间内完成,而且也不会发生退化,是一个非常优秀的算法,时间复杂 ...

随机推荐

  1. [原博客] POJ 1704 Georgia and Bob

    题目链接题意:如图,Georgia和Bob在玩游戏.一个无限长的棋盘上有N个旗子,第i个棋子的位置可以用Pi表示.现在Georgia先走.每个人每一次可以把一枚棋子向左移动任意个格子,但是不能超越其他 ...

  2. Java ,单实例 多线程 ,web容器,servlet与struts1-2.x系列,线程安全的解决

    1.Servlet是如何处理多个请求同时访问呢? 回答:servlet是默认采用单实例,多线程的方式进行.只要webapp被发布到web容器中的时候,servlet只会在发布的时候实例化一次,serv ...

  3. ECMall模板开发文档

    ECMall 模板开发文档 前 言 欢迎阅读 ECMall 模板制作教程,通过阅读本教程可快速上手 ECMall 模板的使用和制作. ECMall 模板制 作要求用户具备 XML . XHTML 和 ...

  4. PYTHON常见数据类型示例

    shoplist = ['apple', 'mango', 'carrot', 'banana'] print('I have ', len(shoplist), ' items to purchas ...

  5. 根据指定的commit查找对应的log

    find commit by hash sha in git 问题: I need to find a commit in Git by given hash SHA. For example, if ...

  6. WordPress RokNewsPager插件‘thumb.php’多个安全漏洞

    漏洞名称: WordPress RokNewsPager插件‘thumb.php’多个安全漏洞 CNNVD编号: CNNVD-201309-369 发布时间: 2013-09-24 更新时间: 201 ...

  7. Node.js权威指南 (12) - Node.js中的其他模块

    12.1 使用dns模块解析域名 / 313 12.1.1 使用resolve方法将域名解析为DNS记录 / 313 12.1.2 使用lookup方法查询IP地址 / 315 12.1.3 使用re ...

  8. Windows SharePoint Services 默认母版页

    转:http://msdn.microsoft.com/zh-cn/library/ms467402(v=office.12).aspx 最终用户可以自定义的 Windows SharePoint S ...

  9. 数学类杂志SCI2013-2014影响因子

    ISSN Abbreviated Journal Title Full Title Category Subcategory Country total Cites IF        2013-20 ...

  10. [转]js动态创建json类型

    废话少说:json是一个特有的键值对数组类型.既然是数组类型那么我们就可以这样定义 1.先定义数组 var Data = []; 2.理解键值对 对象名:值{ "id": i, & ...