Boyer-Moore算法
1、概述
在用于查找子字符串的算法当中,BM(Boyer-Moore)算法是目前相当有效又容易理解的一种,一般情况下,比KMP算法快3-5倍。
BM算法在移动模式串的时候是从左到右,而进行比较的时候是从右到左的。
常规的匹配算法移动模式串的时候是从左到右,而进行比较的时候也是是从左到右的,基本框架是:
- j = 0;
- while(j <= strlen(主串)- strlen(模式串)){
- for (i = 0;i < strlen(模式串) && 模式串[i] == 主串[i + j]; ++i)
- ;
- if (i == strlen(模式串))
- Match;
- else
- ++j;
- }
而BM算法在移动模式串的时候是从左到右,而进行比较的时候是从右到左的,基本框架是:
- j = 0;
- while (j <= strlen(主串) - strlen(模式串)) {
- for (i = strlen(模式串) - 1; i >= 0 && 模式串[i] ==主串[i + j]; --i)
- if (i < 0)
- match;
- else
- ++j;
- }
显然BM算法并不是上面那个样子,BM算法的精华就在于++j
2、BM算法思想
BM算法实际上包含两个并行的算法,坏字符算法和好后缀算法。这两种算法的目的就是让模式串每次向右移动尽可能大的距离(j+=x,x尽可能的大)。
几个定义:
例主串和模式串如下:
主串 : mahtavaatalomaisema omalomailuun
模式串: maisemaomaloma
好后缀:模式串中的aloma为“好后缀”。
坏字符:主串中的“t”为坏字符。
好后缀算法
如果程序匹配了一个好后缀, 并且在模式中还有另外一个相同的后缀, 那
把下一个后缀移动到当前后缀位置。好后缀算法有两种情况:
Case1:模式串中有子串和好后缀安全匹配,则将最靠右的那个子串移动到好后缀的位置。继续进行匹配。
Case2:如果不存在和好后缀完全匹配的子串,则在好后缀中找到具有如下特征的最长子串,使得P[m-s…m]=P[0…s]。说不清楚的看图。
坏字符算法
当出现一个坏字符时, BM算法向右移动模式串, 让模式串中最靠右的对应字符与坏字符相对,然后继续匹配。坏字符算法也有两种情况。
Case1:模式串中有对应的坏字符时,见图。
Case2:模式串中不存在坏字符。见图。
移动规则
BM算法的移动规则是:
将概述中的++j,换成j+=MAX(shift(好后缀),shift(坏字符)),即
BM算法是每次向右移动模式串的距离是,按照好后缀算法和坏字符算法计算得到的最大值。
shift(好后缀)和shift(坏字符)通过模式串的预处理数组的简单计算得到。好后缀算法的预处理数组是bmGs[],坏字符算法的预处理数组是BmBc[]。
3、代码分析
定义
BM算法子串比较失配时,按坏字符算法计算模式串需要向右移动的距离,要借助BmBc数组。
注意BmBc数组的下标是字符,而不是数字。
BmBc数组的定义,分两种情况。
1、 字符在模式串中有出现。如下图,BmBc[‘k’]表示字符k在模式串中最后一次出现的位置,距离模式串串尾的长度。
2、 字符在模式串中没有出现:,如模式串中没有字符p,则BmBc[‘p’] = strlen(模式串)。
BM算法子串比较失配时,按好后缀算法计算模式串需要向右移动的距离,要借助BmGs数组。
BmGs数组的下标是数字,表示字符在模式串中位置。
BmGs数组的定义,分三种情况。
1、 对应好后缀算法case1:如下图:i是好后缀之前的那个位置。
2、 对应好后缀算法case2:如下图所示:
3、 当都不匹配时,BmGs[i] = strlen(模式串)
在计算BmGc数组时,为提高效率,先计算辅助数组Suff。
Suff数组的定义:suff[i] = 以i为边界, 与模式串后缀匹配的最大长度,即P[i-s...i]=P[m-s…m]如下图:
举例如下:
分析
用Suff[]计算BmGs的方法。
1) BmGs[0…m-1] = m;(第三种情况)
2) 计算第二种情况下的BmGs[]值:
for(i=0;i
if(-1==i || Suff[i] == i+1)
for(;j < m-1-i;++j)
if(suff[j] == m)
BmGs[j] = m-1-i;
3) 计算第三种情况下BmGs[]值,可以覆盖前两种情况下的BmGs[]值:
for(i=0;i
BmGs[m-1-suff[i]] = m-1-i;
如下图所示:
Suff[]数组的计算方法。
常规的方法:如下,很裸很暴力。
Suff[m-1]=m;
for(i=m-2;i>=0;--i){
q=i;
while(q>=0&&P[q]==P[m-1-i+q])
--q;
Suff[i]=i-q;
}
有聪明人想出一种方法,对常规方法进行改进。基本的扫描都是从右向左。改进的地方就是利用了已经计算得到的suff[]值,计算现在正在计算的suff[]值。
如下图所示:
i是当前正准备计算的suff[]值得那个位置。
f是上一个成功进行匹配的起始位置(不是每个位置都能进行成功匹配的, 实际上能够进行成功匹配的位置并不多)。
q是上一次进行成功匹配的失配位置。
如果i在q和f之间,那么一定有P[i]=P[m-1-f+i];并且如果suff[m-1-f+i]=i-q, suff[i]和suff[m-1-f+i]就没有直接关系了。
代码
- void preBmBc(char *x, int m, int bmBc[]) {
- int i;
- for (i = 0; i < ASIZE; ++i)
- bmBc[i] = m;
- for (i = 0; i < m - 1; ++i)
- bmBc[x[i]] = m - i - 1;
- }
- void suffixes(char *x, int m, int *suff) {
- int f, g, i;
- f = 0;
- suff[m - 1] = m;
- g = m - 1;
- for (i = m - 2; i >= 0; --i) {
- if (i > g && suff[i + m - 1 - f] < i - g)
- suff[i] = suff[i + m - 1 - f];
- else {
- if (i < g)
- g = i;
- f = i;
- while (g >= 0 && x[g] == x[g + m - 1 - f])
- --g;
- suff[i] = f - g;
- }
- }
- }
- void preBmGs(char *x, int m, int bmGs[]) {
- int i, j, suff[XSIZE];
- suffixes(x, m, suff);
- for (i = 0; i < m; ++i)
- bmGs[i] = m;
- j = 0;
- for (i = m - 1; i >= 0; --i)
- if (suff[i] == i + 1)
- for (; j < m - 1 - i; ++j)
- if (bmGs[j] == m)
- bmGs[j] = m - 1 - i;
- for (i = 0; i <= m - 2; ++i)
- bmGs[m - 1 - suff[i]] = m - 1 - i;
- }
- void BM(char *x, int m, char *y, int n) {
- int i, j, bmGs[XSIZE], bmBc[ASIZE];
- /* Preprocessing */
- preBmGs(x, m, bmGs);
- preBmBc(x, m, bmBc);
- /* Searching */
- j = 0;
- while (j <= n - m) {
- for (i = m - 1; i >= 0 && x[i] == y[i + j]; --i);
- if (i < 0) {
- OUTPUT(j);
- j += bmGs[0];
- }
- else
- j += MAX(bmGs[i], bmBc[y[i + j]] - m + 1 + i);
- }
- }
- void preBmBc(char *x, int m, int bmBc[]) {
- int i;
- for (i = 0; i < ASIZE; ++i)
- bmBc[i] = m;
- for (i = 0; i < m - 1; ++i)
- bmBc[x[i]] = m - i - 1;
- }
- void suffixes(char *x, int m, int *suff) {
- int f, g, i;
- f = 0;
- suff[m - 1] = m;
- g = m - 1;
- for (i = m - 2; i >= 0; --i) {
- if (i > g && suff[i + m - 1 - f] < i - g)
- suff[i] = suff[i + m - 1 - f];
- else {
- if (i < g)
- g = i;
- f = i;
- while (g >= 0 && x[g] == x[g + m - 1 - f])
- --g;
- suff[i] = f - g;
- }
- }
- }
- void preBmGs(char *x, int m, int bmGs[]) {
- int i, j, suff[XSIZE];
- suffixes(x, m, suff);
- for (i = 0; i < m; ++i)
- bmGs[i] = m;
- j = 0;
- for (i = m - 1; i >= 0; --i)
- if (suff[i] == i + 1)
- for (; j < m - 1 - i; ++j)
- if (bmGs[j] == m)
- bmGs[j] = m - 1 - i;
- for (i = 0; i <= m - 2; ++i)
- bmGs[m - 1 - suff[i]] = m - 1 - i;
- }
- void BM(char *x, int m, char *y, int n) {
- int i, j, bmGs[XSIZE], bmBc[ASIZE];
- /* Preprocessing */
- preBmGs(x, m, bmGs);
- preBmBc(x, m, bmBc);
- /* Searching */
- j = 0;
- while (j <= n - m) {
- for (i = m - 1; i >= 0 && x[i] == y[i + j]; --i);
- if (i < 0) {
- OUTPUT(j);
- j += bmGs[0];
- }
- else
- j += MAX(bmGs[i], bmBc[y[i + j]] - m + 1 + i);
- }
- }
Boyer-Moore算法的更多相关文章
- Boyer Moore算法(字符串匹配)
上一篇文章,我介绍了KMP算法. 但是,它并不是效率最高的算法,实际采用并不多.各种文本编辑器的"查找"功能(Ctrl+F),大多采用Boyer-Moore算法. Boyer-Mo ...
- Boyer–Moore (BM)字符串搜索算法
在计算机科学里,Boyer-Moore字符串搜索算法是一种非常高效的字符串搜索算法.它由Bob Boyer和J Strother Moore设计于1977年.此算法仅对搜索目标字符串(关键字)进行预处 ...
- Leetcode OJ : Implement strStr() [ Boyer–Moore string search algorithm ] python solution
class Solution { public: int strStr(char *haystack, char *needle) { , skip[]; char *str = haystack, ...
- Boyer-Moore 字符串匹配算法
字符串匹配问题的形式定义: 文本(Text)是一个长度为 n 的数组 T[1..n]: 模式(Pattern)是一个长度为 m 且 m≤n 的数组 P[1..m]: T 和 P 中的元素都属于有限的字 ...
- DPI (Deep Packet Inspection) 深度包检测技术
详解DPI与网络回溯分析技术 随着网络通讯技术进步与发展,网络通讯已跨入大数据时代,如何监控各类业务系统的通讯数据在大数据流量中传输质量,以及针对海量的网络通讯数据的范畴中存在少量的恶意流量的检测,避 ...
- 【Java字符序列】Pattern
简介 Pattern,正则表达式的编译表示,操作字符序列的利器. 整个Pattern是一个树形结构(对应于表达式中的‘|’),一般为链表结构,树(链表)的基本元素是Node结点,Node有各种各样的子 ...
- DPI深度报文检测架构及关键技术实现
DPI深度报文检测架构及关键技术实现 当前DPI(Deep Packet Inspect深度报文识别)技术是安全领域的关键技术点之一,围绕DPI技术衍生出的安全产品类型也非常的多样.在分析DPI的进一 ...
- 2019-8-31-C#-对-byte-数组进行模式搜索
title author date CreateTime categories C# 对 byte 数组进行模式搜索 lindexi 2019-08-31 16:55:58 +0800 2018-07 ...
- C# 对 byte 数组进行模式搜索
本文告诉大家几个方法从 byte 数组找到对应的相同序列的数组 最简单的方法是进行数值判断,但是代码最少是使用Linq ,效率比较高是使用 Boyer-Moore 算法,下面就告诉大家几个算法的代码 ...
- 我熬夜读完这份“高分宝典”,竟4面拿下字节跳动offer
前言 怎样的契机? 实际上,目前毕业已经两年时间了,在大学时就已经开始关注字节跳动的发展.一开始,我是电气自动化专业的,大二清楚目标之后就转计算机了,大四进了一家小型的互联网公司实习,具体就不说哪家了 ...
随机推荐
- EBS开发性能优化之SQL语句优化
(1)选择运算 尽可能先做选择运算,这是优化策略中最重要.最基本的一条,选择运算一般会使计算的中间结果大大变小,在对同一表格进行多个选择运算时,选择条件的排列顺序对性能也有很大影响,因为排列顺序不仅影 ...
- BASH如何获得某个目录下的某类文件的文件名
假设某个目录下有一堆以jpeg为后缀的文件名,我们需要在另一个目录中获得他们的文件名,并输出. 可以联合使用ls,awk,sed等命令来完成. 方法一: 使用ls列出目录下以.jpeg为结尾的文件,然 ...
- Matlab:如何查找给定目录下的文件
我们有很多目录,每个目录下都有些有用的文件,比如图像文件,如何自动的扫描这些文件呢? 可以使用dir函数来完成这个任务. 比如假设给定目录 baseDir,它是一个字符串,包含的是某个目录,例如'./ ...
- 剑指Offer——线程同步volatile与synchronized详解
(转)Java面试--线程同步volatile与synchronized详解 0. 前言 面试时很可能遇到这样一个问题:使用volatile修饰int型变量i,多个线程同时进行i++操作,这样可以实现 ...
- Android动态加载入坑指南
曾几何时,国内各大公司掀起了一股研究Android动态加载的技术,两年多过去了,动态加载技术俨然成了Android开发中必须掌握的技术.那么动态加载技术是什么呢,这里谈谈我的个人看法,如有雷同,纯属偶 ...
- RecyclerView下拉刷新上拉加载(一)
listview下拉刷新上拉加载扩展(一) http://blog.csdn.net/baiyuliang2013/article/details/50252561 listview下拉刷新上拉加载扩 ...
- Spark:大数据的电花火石!
什么是Spark?可能你很多年前就使用过Spark,反正当年我四六级单词都是用的星火系列,没错,星火系列的洋名就是Spark. 当然这里说的Spark指的是Apache Spark,Apache Sp ...
- Android开发学习之路--传感器之初体验
说到传感器,还是有很多的,有加速度啊,光照啊,磁传感器等等.当然android手机之所以称为智能手机,少不了这几款传感器的功劳了.下面就学习下了,这里主要学习光照,加速度和磁. 新建工程emSenso ...
- SQL 2012 Always On 为 MSCRMSqlClrLogin SQL 登录名创建非对称密钥时报语法错误
根据实施手册中的下图的SQL在为MSCRMSqlClrLogin 创建非对称秘钥时报语法错误,具体的错误在就path那,调了多次还是报错,索性就把SQL拆开执行. 先执行这条 SELECT * FRO ...
- 【一天一道LeetCode】#125. Valid Palindrome
一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given a ...