BoyerMoore算法详解(理解不了你打死我)|代码实现(c++)

2023-11-11 15:20

本文主要是介绍BoyerMoore算法详解(理解不了你打死我)|代码实现(c++),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

 

1.BoyerMoore算法

1.1坏字符规则:

1.2好后缀规则

1.3一个例子

2.代码实现


1.BoyerMoore算法

BoyerMoore算法和KMP、BF算法一样,是一种字符串匹配的算法,不过它的效率比KMP算法更为高效(3~5倍)。

我们先来简单介绍一下BF算法的几个规则(特点)

  1. 首先,将模式串和目标串(要进行查找的串)左端对齐,从右端开始比较。
  2. 当比较的字符不匹配时,有两种规则。

1.1坏字符规则:

如上图:我们发现目标串的一个字符和模式串的对应的位置的字符不匹配,我们称这种现象为坏字符现象。这时我们首先在目标串中查找模式串中对应位置不匹配的字符的位置,简单的说,就是在abc中查找d,发现d不存在,我们记d在目标串中的位置为-1.然后用目标串不匹配的位置减去坏字符在目标串里出现的另一个位置(最右),如上2-(-1)=3(计数从0开始),这时我们将目标串向右移动3,继续比较,这样的规则就是坏字符规则。

后移位数 = 坏字符的位置 - 搜索词中的上一次出现位置
如果”坏字符”不包含在搜索词之中,则上一次出现位置为 -1。

再来一个例子让你理解:

再来:

P和E不匹配,P在目标串中上次出现的位置为4,移动距离为6-4=2.

上述就是坏字符规则,我们接着介绍好“好后缀规则”

1.2好后缀规则

看下图:

我们从后往前依次比较,发现MPLE都匹配上了,I和A没匹配上,这时如果我们按照上述的坏规则法,应该向向右移动(2-(-1)=3)个位置,移动后的结果为:

这时我们发现向后目标串只向后移动了三位。在这里我们介绍好后缀规则,这种规则能使目标串更快速的向后移动。

我们将两个字符串回归到下面这种状态:

可以发现,后面的MPLE都配对上了,这时我们成PLE,LE,和E都为好后缀,这种我们称为好后缀现象。

好后缀规则就是:后移位数=好后缀出现的位置(以最后一个字符为准)-好后缀上一次出现的位置(除过后缀的最右边)

如果后缀不存在,那么记上一次的出现位置为-1.

我们发现只有后缀E在目标串里出现了两次,那么后移位数为:6-0 =6位。移动之后的状态为:

可以发现,比坏字符规则多移了三位。

需要注意的一点是:在Boyer-Moore算法中,并不是好后缀的移动距离一定大于坏规则的移动距离,当出现好后缀现象的时候,我们可以将好后缀的移动距离和坏规则的移动距离进行比较,取较大的来移动目标串。

再来一个例子:

我们假设匹配状态如下图:

我们发现e和s不匹配,并且有好后缀abc,bc,c。

这时我们发现abc,bc,c都在前面出现了,这时候就可以解释为什么要以好后缀的最后一个字符为准的原因了。因为无论怎么多长的好后缀,它一定是以目标串的最后一个字符结尾的。即上述的好后缀都是以c结尾。所以我们可以直接以最后一个字符为参考值来确定好后缀规则的移动位数,这时移动位数应为:5 - 1 =4,移动之后如下图:

当然这个匹配最终是失败的,我们不要在乎那些只要明白好后缀的移动规则即可。

经过上面的这些说明,我们其实可以发现,如果末尾字符只出现在目标串的头和尾,或者只存在于末尾的话,那么每次好后缀规则向前移动的距离是相同的。

其实我们可以提前根据目标串确定坏字符表(存储坏字符规则的移动距离)以及好后缀表,好后缀表如下有几个例子

xeapmple的后缀表应该为: (从右到左)8, 7, 7 ,7 ,7 ,7 ,7, 7, 8

example的后缀表应该为:6,6,6,6,6,7

abc的后缀表应该为:3,3,3(此时和坏规则一模一样)

pleple的后缀表应该为:3,3,3,3,6,6

我们其实可以发现,最末尾字符的坏规则移动距离和好后缀方法是一样的,这也算是这个算法的一个巧妙之处,每一次都会让目标串移动到确定的距离,且两种移动规则在某种条件下的移动距离是相同的。

1.3一个例子

介绍完上面两个规则,我们来看一个整体的匹配例子。(来源于阮一峰的分享)

第一步:

发现S和E不匹配,应用坏规则,向后移动6-(-1)=7位。

第二步:

P和E不匹配,且P在目标串中出现,后移位= 6-4 = 2

第三步:

依次向前比较,发现I和A不匹配:

坏规则向前移动3位

好后缀向前移动6位

应用好后缀,向前移动6位。

第四步:

P和E不匹配,应用坏规则6-4=2位,向右移动二位。

第五步:

依次比较,匹配成功。

 

2.代码实现

如果我上面解释的还不够清楚,请看我的代码。

代码运行结果:

# include<iostream>
# include<string>
using namespace std;
//最长匹配长度
# define MAX_SIZE 1000//得到后缀规则的移动距离
int GoodSuffix(int j, string& pat) {int terminal = pat.length() - 1;int index = -1;j--;while (j >= 0) {if (pat[j] == pat[terminal]) {index = j;break;}else j--;}return terminal - index;
}
//得到坏字符规则的移动距离
int BadChar(int j,char temp,string str)
{int index = -1;for (int i = j - 1; i >= 0; --i){if (str[i] == temp){index = i;break;}}return j - index;
}
int BM(string source, string target)
{int i = 0, j = 0, soulen = source.length(), tarlen = target.length();  //初始化int badvalue = 0, distance = 0;if (soulen < tarlen) {  //比较长度printf("字符串的长度小于搜索词的长度\n");return -1;}i = tarlen - 1; j = tarlen - 1; //从尾开始匹配while (i < soulen) {if (source[i] == target[j]) {  //字符匹配成功if (j == 0) {printf("匹配成功\n");return i;}i--; j--;}else {  //未匹配成功if (j == tarlen - 1) {  //如果尾字符未匹配成功,"坏字符规则"badvalue = BadChar(j, source[i],target);cout << "坏字符移动:" << badvalue << endl;i = i + tarlen - 1 - j + badvalue;j = tarlen - 1;}else {  //有后缀,比较"坏字符规则"和"后缀规则"badvalue = BadChar(j, source[i], target);if (badvalue == -1)badvalue = target.length();distance = badvalue > GoodSuffix(j,target) ? badvalue : GoodSuffix(j, target);cout << "好后缀为:" << GoodSuffix(j, target) << "  坏字符为:" << badvalue << "  比较之后移动" << distance << endl;i = i + tarlen - 1 - j + distance;  //更新i的位置j = tarlen - 1;   //更新j位置}}}
}
int main()
{//好后缀表:存储了当遇到好后缀时,目标串向右的移动距离。int* goodSuffix = new int[MAX_SIZE];string model = "THERE IS A SIMPLE EXAMPLE";string str = "EXAMPLE";cout << "模式串" << model << endl;cout << "目标串" << str << endl;cout << str << "的好后缀表为:" << endl;for (int i = 0; i < str.length(); ++i){cout << str[i] << ":" << GoodSuffix(i,str) << endl;;}cout << "匹配成功的开始位置为:" << BM(model, str) << endl;delete[]goodSuffix;return 0;
}

 

这篇关于BoyerMoore算法详解(理解不了你打死我)|代码实现(c++)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/390998

相关文章

pytorch自动求梯度autograd的实现

《pytorch自动求梯度autograd的实现》autograd是一个自动微分引擎,它可以自动计算张量的梯度,本文主要介绍了pytorch自动求梯度autograd的实现,具有一定的参考价值,感兴趣... autograd是pytorch构建神经网络的核心。在 PyTorch 中,结合以下代码例子,当你

SpringBoot集成Milvus实现数据增删改查功能

《SpringBoot集成Milvus实现数据增删改查功能》milvus支持的语言比较多,支持python,Java,Go,node等开发语言,本文主要介绍如何使用Java语言,采用springboo... 目录1、Milvus基本概念2、添加maven依赖3、配置yml文件4、创建MilvusClient

python logging模块详解及其日志定时清理方式

《pythonlogging模块详解及其日志定时清理方式》:本文主要介绍pythonlogging模块详解及其日志定时清理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录python logging模块及日志定时清理1.创建logger对象2.logging.basicCo

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

前端CSS Grid 布局示例详解

《前端CSSGrid布局示例详解》CSSGrid是一种二维布局系统,可以同时控制行和列,相比Flex(一维布局),更适合用在整体页面布局或复杂模块结构中,:本文主要介绍前端CSSGri... 目录css Grid 布局详解(通俗易懂版)一、概述二、基础概念三、创建 Grid 容器四、定义网格行和列五、设置行

Node.js 数据库 CRUD 项目示例详解(完美解决方案)

《Node.js数据库CRUD项目示例详解(完美解决方案)》:本文主要介绍Node.js数据库CRUD项目示例详解(完美解决方案),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考... 目录项目结构1. 初始化项目2. 配置数据库连接 (config/db.js)3. 创建模型 (models/

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

SQL表间关联查询实例详解

《SQL表间关联查询实例详解》本文主要讲解SQL语句中常用的表间关联查询方式,包括:左连接(leftjoin)、右连接(rightjoin)、全连接(fulljoin)、内连接(innerjoin)、... 目录简介样例准备左外连接右外连接全外连接内连接交叉连接自然连接简介本文主要讲解SQL语句中常用的表

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

Python中局部变量和全局变量举例详解

《Python中局部变量和全局变量举例详解》:本文主要介绍如何通过一个简单的Python代码示例来解释命名空间和作用域的概念,它详细说明了内置名称、全局名称、局部名称以及它们之间的查找顺序,文中通... 目录引入例子拆解源码运行结果如下图代码解析 python3命名空间和作用域命名空间命名空间查找顺序命名空