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

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof