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

BM算法定义了两个规则:

1、坏字符规则:当文本串中的某个字符跟模式串的某个字符不匹配时,我们称文本串中的这个失配字符为坏字符,此时模式串需要向右移动,移动的位数 = 坏字符在模式串中的位置 - 坏字符在模式串中最右出现的位置。此外,如果"坏字符"不包含在模式串之中,则最右出现位置为-1。
2、好后缀规则:当字符失配时,后移位数 = 好后缀在模式串中的位置 - 好后缀在模式串上一次出现的位置,且如果好后缀在模式串中没有再次出现,则为-1。

关于坏字符规则和好后缀规则的具体讲解,以及怎么移动,可以查看阮一峰老师的详细讲解:http://www.ruanyifeng.com/blog/2013/05/boyer-moore_string_search_algorithm.html

这里根据讲解画了两张图,方便自己理解坏字符规则:

2019年11月14日15:38:41 修改

具体代码如下:

     private static final int SIZE = 256; // 全局变量或者是局部变量

     /**
* 坏字符规则哈希表构建方法
*
* @param b
* 模式串
* @param m
* 模式串的长度
* @param bc
* 散列表
*/
private void generateBC(char[] b, int m, int[] bc) {
for (int i = 0; i < SIZE; ++i) {
bc[i] = -1; // 初始化bc
} for (int i = 0; i < m; ++i) {
int ascii = (int) b[i]; // 计算b[i]的ASCII值
bc[ascii] = i;
}
} /**
* 好后缀规则构建哈希表
*
* @param b
* 模式串
* @param m
* 模式串长度
* @param suffix
* suffix数组的下标 k,表示后缀子串的长度,
* 下标对应的数组值存储的是,在模式串中跟好后缀{u}相匹配的子串{u*}的起始下标值
* @param prefix
* 记录模式串的后缀子串是否能匹配模式串的前缀子串
*/
private void generateGS(char[] b, int m, int[] suffix, boolean[] prefix) {
for (int i = 0; i < m; ++i) { // 初始化
suffix[i] = -1;
prefix[i] = false;
} for (int i = 0; i < m - 1; ++i) {
int j = i;
int k = 0; // 公共后缀子串长度
while (j >= 0 && b[j] == b[m - 1 - k]) { // 与b[0, m-1]求公共后缀子串
--j;
++k;
suffix[k] = j + 1; // j+1表示公共后缀在b[0,i]中的起始下标
}
if (j == -1) {
prefix[k] = true; // 如果公共后缀子串也是模式串的后缀子串
}
}
} /**
* 完整的BM算法 好后缀+坏字符
*
* @param a
* 主串
* @param n
* 主串的长度
* @param b
* 模式串
* @param m
* 模式串的长度
* @return
*/
public int bm(char[] a, int n, char[] b, int m) {
int[] bc = new int[SIZE];
generateBC(b, m, bc); // 构建坏字符哈希表
int[] suffix = new int[m];
boolean[] prefix = new boolean[m];
generateGS(b, m, suffix, prefix); // 构建好字符哈希表
int i = 0; // j 表示主串与模式串匹配的第一个字符
while (i < n - m) {
int j = 0;
for (j = m - 1; j >= 0; --j) {// 模式串从后向前匹配
if (a[i + j] != b[j]) {
break; // 坏字符串
}
}
if (j < 0) {
return i;// 匹配成功,返回主串和模式串第一个匹配字符的位置
}
int x = j - bc[(int) a[i + j]];
int y = 0;
if (j < m - 1) { // 如果有好后缀的话
y = moveByGS(j, m, suffix, prefix);
}
i = i + Math.max(x, y);
}
return -1;
} private int moveByGS(int j, int m, int[] suffix, boolean[] prefix) {
int k = m - 1 - j; // 好后缀的长度
if (suffix[k] != -1) {
return j - suffix[k] + 1;
}
for (int r = j + 2; r <= m - 1; ++r) {
if (prefix[m - r] == true) {
return r;
}
}
return m;
}

现在再看BM算法,原来之前自己是一点也没弄懂!只是当做文章简单读了一遍,阿西吧!

首先,那个坏字符的散列表的构建就没有弄懂:

1、为什么在散列表数组中要初始化每一个值为-1?

这里是在坏字符匹配的时候,如果主串与模式串中字符没有匹配上(把坏字符在模式串中下标记做xi),此时的xi=-1

2、你有没有考虑过模式串中的相同的字符的ASCII码是相同的,那样循环处理的话,只是记录模式串中相同字符中最后面的那个字符的下标,没有问题吗?

这个是没有问题的!这里无非有两种情况,就是坏字符与非坏字符:

坏字符:因为是从后向前倒序匹配,只需要知道后面的字符下标,就可以计算出移动距离

非坏字符:需要去寻找坏字符或者使用好后缀规则

2019年11月14日15:34:37 修改

此大部分内容来自极客时间专栏,王争老师的《数据结构与算法之美》

极客时间:https://time.geekbang.org/column/intro/126

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

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

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

  2. 动画演示Sunday字符串匹配算法——比KMP算法快七倍!极易理解!

    前言 上一篇我用动画的方式向大家详细说明了KMP算法(没看过的同学可以回去看看). 这次我依旧采用动画的方式向大家介绍另一个你用一次就会爱上的字符串匹配算法:Sunday算法,希望能收获你的点赞关注收 ...

  3. 字符串匹配算法之Sunday算法(转)

    字符串匹配算法之Sunday算法 背景 我们第一次接触字符串匹配,想到的肯定是直接用2个循环来遍历,这样代码虽然简单,但时间复杂度却是Ω(m*n),也就是达到了字符串匹配效率的下限.于是后来人经过研究 ...

  4. 字符串匹配算法之 kmp算法 (python版)

    字符串匹配算法之 kmp算法 (python版) 1.什么是KMP算法 KMP是三位大牛:D.E.Knuth.J.H.MorriT和V.R.Pratt同时发现的.其中第一位就是<计算机程序设计艺 ...

  5. 字符串匹配算法之Sunday算法

    字符串匹配查找算法中,最着名的两个是KMP算法(Knuth-Morris-Pratt)和BM算法(Boyer-Moore).两个算法在最坏情况下均具有线性的查找时间.但是在实用上,KMP算法并不比最简 ...

  6. 字符串匹配算法:Sunday算法

    背景 我们第一次接触字符串匹配,想到的肯定是直接用2个循环来遍历,这样代码虽然简单,但时间复杂度却是\(Ω(m*n)\),也就是达到了字符串匹配效率的下限.于是后来人经过研究,构造出了著名的KMP算法 ...

  7. 字符串匹配算法(三)-KMP算法

    今天我们来聊一下字符串匹配算法里最著名的算法-KMP算法,KMP算法的全称是 Knuth Morris Pratt 算法,是根据三位作者(D.E.Knuth,J.H.Morris 和 V.R.Prat ...

  8. 字符串匹配算法之————KMP算法

    上一篇中讲到暴力法字符串匹配算法,但是暴力法明显存在这样一个问题:一次只移动一个字符.但实际上,针对不同的匹配情况,每次移动的间隔可以更大,没有必要每次只是移动一位: 关于KMP算法的描述,推荐一篇博 ...

  9. 字符串匹配算法之kmp算法

    kmp算法是一种效率非常高的字符串匹配算法,是由Knuth,Morris,Pratt共同提出的模式匹配算法,所以简称KMP算法 算法思想 在一个字符串中查找另一个字符串时,会遇到如下图的情况 我们通常 ...

随机推荐

  1. bzoj 1112: [POI2008]砖块Klo【对顶堆】

    priority_queue实现的对顶堆,细节超级多WA了十几次--但是理论上是最简便的orz其实是我已经不会写平衡树了 枚举左端点,显然要把这一段的高度搞成(l,l+k-1)的高度中位数,所以需要一 ...

  2. (图论)51NOD 1298 圆与三角形

    给出圆的圆心和半径,以及三角形的三个顶点,问圆同三角形是否相交.相交输出"Yes",否则输出"No".(三角形的面积大于0).     输入 第1行:一个数T, ...

  3. bzoj1303[CQOI2008]中位数图 / 乱搞

    题目描述 给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b.中位数是指把所有元素从小到大排列后,位于中间的数. 输入输出格式 输入格式: 第一行为两个正整数n和b,第二行为1 ...

  4. SpringMVC分页查询无法直接将对象转换成json的解决办法(报org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type:错)

    在用ajax获得分页数据时,无法将获取的值赋值给input标签,在修改用户信息时不显示用户已经注册的信息,百度可知 springmvc处理分页数据返回的对象时,无法直接将对象转换成json,会报org ...

  5. linux php5.6 提示 could not find driver

    1.进入在PHP源码包中进入ext/pdo_mysql # wget http://pecl.php.net/get/PDO_MYSQL-1.0.2.tgz 2.然后是解压缩. # tar -zxvf ...

  6. [BZOJ4043/CERC2014]Vocabulary

    Description 给你三个字符串,这些字符串有些单词模糊不可认了,用"?"来代表. 现在你可以用任意英文小写字母来代表它们.要求是使得给定的三个字符串中 所有的"? ...

  7. [POI2001]Gra绿色游戏

    Description 绿色游戏是一种两人游戏,双方分别称Ann和Billy.游戏的内容主要是轮流在棋盘上移动一颗棋子.棋盘上的点一部分是绿色的,其余是白色的:全部从1至a+b编号.编号1至a的点属于 ...

  8. poj 2506 Tiling 递推

    题目链接: http://poj.org/problem?id=2506 题目描述: 有2*1和2*2两种瓷片,问铺成2*n的图形有多少种方法? 解题思路: 利用递推思想,2*n可以由2*(n-1)的 ...

  9. Linux tcpdump命令英文文档

    https://www.computerhope.com/unix/tcpdump.htm

  10. 201 Bitwise AND of Numbers Range 数字范围按位与

    给定范围 [m,n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含m, n两端点).例如,给定范围 [5,7],您应该返回 4. 详见 ...