字符串匹配算法(BM)

文章目录

    • 1. BM(Boyer-Moore)算法
      • 1.1 坏字符规则
      • 1.2 好后缀规则
      • 1.3 两种规则如何选择
    • 2. BM算法代码实现
      • 2.1 坏字符
      • 2.2 好后缀
      • 2.3 完整代码
      • 2.4 调试
    • 3. 总结

1. BM(Boyer-Moore)算法

  • 思想:有模式串中不存在的字符,那么肯定不匹配,往后多移动几位,提高效率
    在这里插入图片描述
  • BM原理:坏字符规则,好后缀规则

1.1 坏字符规则

在这里插入图片描述

  • 利用坏字符规则,BM算法在最好情况下的时间复杂度非常低,是O(n/m)。比如,主串是aaabaaabaaabaaab,模式串是aaaa。每次比对,模式串都可以直接后移四位,所以,匹配具有类似特点的模式串和主串的时候,BM算法非常高效。
  • 单纯使用坏字符规则还是不够的。因为根据 si-xi计算出来的移动位数有可能是负数,比如主串是aaaaaaaaaaaaaaaa,模式串是baaa。不但不会向后滑动模式串,还有可能倒退。所以,BM算法还需要用到“好后缀规则”。

1.2 好后缀规则

在这里插入图片描述
从好后缀的后缀子串中,找一个最长的且和模式串的前缀子串匹配的 {v},滑动至 {v} 对齐
在这里插入图片描述

1.3 两种规则如何选择

  • 分别计算好后缀和坏字符规则往后滑动的位数,取大的,作为滑动位数(还可以避免负数)

2. BM算法代码实现

2.1 坏字符

  • 找到坏字符在模式串中的位置(有重复的,则是靠后的那个)
    采用哈希,而不是遍历。
    在这里插入图片描述
#define SIZE 256    //字符集字符数
void generateBadChar(char *b, int m, int *badchar)//(模式串字符b,模式串长度m,模式串的哈希表)
{int i, ascii;for(i = 0; i < SIZE; ++i){badchar[i] = -1;//哈希表初始化为-1}for(i = 0; i < m; ++i){ascii = int(b[i]);  //计算字符的ASCII值badchar[ascii] = i;//重复字符被覆盖,记录的是最后出现的该字符的位置}
}

在这里插入图片描述

int str_bm(char *a, int n, char *b, int m)
//只考虑坏字符方法的程序框架
{int *badchar = new int [SIZE];//记录模式串中每个字符最后出现的位置generateBadChar(b,m,hash);     //构建坏字符哈希表int i = 0, j;while(i < n-m+1){for(j = m -1; j >= 0; --j)  //模式串从后往前匹配{if(a[i+j] != b[j])break;  //坏字符对应模式串中的下标是j}if(j < 0)   //匹配成功{return i;   //返回主串与模式串第一个匹配的字符的位置}//这里等同于将模式串往后滑动 j-badchar[int(a[i+j])] 位i = i + (j - badchar[int(a[i+j])]);}return -1;
}

2.2 好后缀

  • 在模式串中,查找跟好后缀匹配的另一个子串
  • 在好后缀的后缀子串中,查找最长的、能跟模式串前缀子串匹配的后缀子串

不考虑效率的话,上面两个操作都可以暴力查找;
解决办法: 预先对模式串进行处理。
在这里插入图片描述
在这里插入图片描述
实现过程:
在这里插入图片描述
预处理模式串,填充suffix,prefix

void generateGS(char *b, int m, int *suffix, bool *prefix)
//预处理模式串,填充suffix,prefix
{int i, j, k;for(i = 0; i < m; ++i)//两个数组初始化{suffix[i] = -1;prefix[i] = false;}for(i = 0; i < m-1; ++i)//b[0,i]{j = i;k = 0;//公共后缀子串长度(模式串尾部取k个出来,分别比较)while(j >= 0 && b[j] == b[m-1-k])//与b[0,m-1]求公共后缀子串{--j;++k;suffix[k] = j+1;//相同后缀子串长度为k时,该子串在b[0,i]中的起始下标// (如果有多个相同长度的子串,被赋值覆盖,存较大的)}if(j == -1)//查找到模式串的头部了prefix[k] = true;//如果公共后缀子串也是模式串的前缀子串}
}

计算滑动位数

  • case1:
    在这里插入图片描述
  • case2:
    在这里插入图片描述
  • case3:(以上都不成立,移动整个模式串(长度m))
    在这里插入图片描述

2.3 完整代码

/*** @description: 字符匹配BM算法* @author: michael ming* @date: 2019/6/18 22:19* @modified by: */
#include <algorithm>
#include <string>
#include <iostream>using namespace std;
#define SIZE 256    //字符集字符数
void generateBadChar(char *b, int m, int *badchar)//(模式串字符b,模式串长度m,模式串的哈希表)
{int i, ascii;for(i = 0; i < SIZE; ++i){badchar[i] = -1;//哈希表初始化为-1}for(i = 0; i < m; ++i){ascii = int(b[i]);  //计算字符的ASCII值badchar[ascii] = i;//重复字符被覆盖,记录的是最后出现的该字符的位置}
}
void generateGS(char *b, int m, int *suffix, bool *prefix)//预处理模式串,填充suffix,prefix
{int i, j, k;for(i = 0; i < m; ++i)//两个数组初始化{suffix[i] = -1;prefix[i] = false;}for(i = 0; i < m-1; ++i)//b[0,i]{j = i;k = 0;//公共后缀子串长度(模式串尾部取k个出来,分别比较)while(j >= 0 && b[j] == b[m-1-k])//与b[0,m-1]求公共后缀子串{--j;++k;suffix[k] = j+1;//相同后缀子串长度为k时,该子串在b[0,i]中的起始下标// (如果有多个相同长度的子串,被赋值覆盖,存较大的)}if(j == -1)//查找到模式串的头部了prefix[k] = true;//如果公共后缀子串也是模式串的前缀子串}
}
int moveByGS(int j, int m, int *suffix, bool *prefix)//传入的j是坏字符对应的模式串中的字符下标
{int k = m - 1 - j;//好后缀长度if(suffix[k] != -1)//case1,找到跟好后缀一样的模式子串(多个的话,存的靠后的那个(子串起始下标))return j - suffix[k] + 1;for(int r = j + 2; r < m; ++r)//case2{if(prefix[m-r] == true)//m-r是好后缀的子串的长度,如果这个好后缀的子串是模式串的前缀子串return r;//在上面没有找到相同的好后缀下,移动r位,对齐前缀到好后缀}return m;//case3,都没有匹配的,移动m位(模式串长度)
}
int str_bm(char *a, int n, char *b, int m)//a表示主串,长n; b表示模式串,长m
{int *badchar = new int [SIZE];//记录模式串中每个字符最后出现的位置generateBadChar(b,m,badchar);     //构建坏字符哈希表int *suffix = new int [m];bool *prefix = new bool [m];generateGS(b, m, suffix, prefix);   //预处理模式串,填充suffix,prefixint i = 0, j, moveLen1, moveLen2;//j表示主串与模式串匹配的第一个字符while(i < n-m+1){for(j = m -1; j >= 0; --j)  //模式串从后往前匹配{if(a[i+j] != b[j])break;  //坏字符对应模式串中的下标是j}if(j < 0)   //匹配成功{delete [] badchar;delete [] suffix;delete [] prefix;return i;   //返回主串与模式串第一个匹配的字符的位置}//这里等同于将模式串往后滑动 j-badchar[int(a[i+j])] 位moveLen1 = j - badchar[int(a[i+j])];//按照坏字符规则移动距离moveLen2 = 0;if(j < m-1)//如果有好后缀的话{moveLen2 = moveByGS(j,m,suffix,prefix);//按照好后缀规则移动距离}i = i + max(moveLen1,moveLen2);//取大的移动}delete [] badchar;delete [] suffix;delete [] prefix;return -1;
}int main()
{string a = "abcacabcbcbacabc", b = "cbacabc";cout << a << "中第一次出现" << b << "的位置(从0开始)是:" << str_bm(&a[0],a.size(),&b[0],b.size());return 0;
}

在这里插入图片描述

2.4 调试

为方便调试,将字符集SIZE改为3,ascii = int(b[i]-'a')

  • 坏字符在模式串中的位置(靠后的那个)
    badchar[0]:a是4
    badchar[1]:b是5
    badchar[2]:c是6
  • 预处理模式串
    在这里插入图片描述
  • 按规则移动
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3. 总结

  • BM算法的内存消耗
    整个算法用到了额外的3个数组,其中bc数组的大小跟字符集大小有关,suffix数组和prefix数组的大小跟模式串长度m有关。
    如果处理字符集很大的字符串匹配问题,badchar数组对内存的消耗就会比较多。
    因为好后缀坏字符规则是独立的,如果运行的环境对内存要求苛刻,可以只使用好后缀规则,不使用坏字符规则,就可以避免badchar数组过多的内存消耗。不过,单纯使用好后缀规则的BM算法效率就会下降一些了。
  • 时间复杂度
    以上BM算法是个初级版本。这个版本,在极端情况下,预处理计算suffix数组、prefix数组的性能会比较差。
    比如模式串是aaaaaaa这种包含很多重复的字符的模式串,预处理的时间复杂度就是O(m^2)。如何优化这种极端情况下的时间复杂度退化,以后再找空研究。
    实际上,BM算法的时间复杂度分析起来是非常复杂,论文"A new proof of the linearity of the Boyer-Moore string searching algorithm"证明了在最坏情况下,BM算法的比较次数上限是5n。论文"Tight bounds on the complexity of the Boyer-
    Moore string matching algorithm"证明了在最坏情况下,BM算法的比较次数上限是3n。

  • BM算法核心思想是,利用模式串本身的特点,在模式串中某个字符与主串不能匹配的时候,将模式串往后多滑动几位,以此来减少不必要的字符比较提高匹配的效率
  • BM算法构建的规则有两类,坏字符规则和好后缀规则。
  • 好后缀规则可以独立于坏字符规则使用。
  • 因为坏字符规则的实现比较耗内存,为了节省内存,我们可以只用好后缀规则来实现BM算法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/480221.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL 数据分析常用语句

1 基础查询 • 2 字符串数字日期时间 • 3 聚合数据查询 • 4 子查询 • 5 联接组合查询 • 6 高级查询 • 7 更新数据 阅读提醒&#xff1a;点击图片放大可看清晰的 1 基础查询 2 字符串数字日期时间 3 聚合数据查询 4 子查询 5 联接组合查询 6 高级查询 7 更新数据 参考资料 …

开源词法分析工具LAC重大升级!打造属于自己的个性化分词器!

星标/置顶小屋&#xff0c;带你解锁最萌最前沿的NLP、搜索与推荐技术文 | 匿名大佬编 | 北大小才女小轶本文将为大家介绍如何使用百度开源的词法分析工具LAC实现个性化分词&#xff08;附代码&#xff09;&#xff0c;并展示LAC在各数据集上惊艳的性能表现。背景介绍分词是中文…

老刘说NLP:这几年的NLP历程回顾,以及关于NLP(知识图谱等)落地的思考

得语言者&#xff0c;得天下。得语言资源者&#xff0c;分天下。得语言逻辑者&#xff0c;争天下。 前言 只有把自己所思考的&#xff0c;所认识的东西&#xff0c;以文字的方式记录下来&#xff0c;才是对自己成长最好的记录方式。 是的&#xff0c;距离上一次动手写博客或者…

Redis系列教程(一):Redis缓存的设计、性能、应用与数据集群同步

Redis 是完全开源免费的&#xff0c;遵守BSD协议&#xff0c;是一个高性能的key-value数据库。Redis本质上是一个Key-Value类型的内存数据库&#xff0c;很像memcached&#xff0c;整个数据库统统加载在内存当中进行操作&#xff0c;定期通过异步操作把数据库数据flush到硬盘上…

论文浅尝 | 提取计数量词丰富知识库

OpenKG 祝各位读者新年快乐&#xff01;论文标题&#xff1a;Enriching Knowledge Bases with Counting Quantifiers论文链接&#xff1a;https://link.springer.com/content/pdf/10.1007%2F978-3-030-00671-6_11.pdf发表会议&#xff1a;ISWC 2018论文源码&#xff1a;https:/…

RNN循环神经网络(吴恩达《序列模型》笔记一)

1、为什么选择序列模型 2、数学符号 用1来代表人名&#xff0c;0来代表非人名&#xff0c;句子x便可以用y[1 1 0 1 1 0 0 0 0]来表示 3、循环网络模型 值得一提的是&#xff0c;共享特征还有助于减少神经网络中的参数数量&#xff0c;一定程度上减小了模型的计算复杂度。 …

字符串匹配算法(KMP)

文章目录1. KMP由来2. KMP算法基本原理3. 代码4. Leetcode 28. 实现 strStr()1. KMP由来 上一节说的BM算法是最高效、最常用的字符串匹配算法。最知名的却是KMP&#xff0c;它3位作者&#xff08;D.E.Knuth&#xff0c;J.H.Morris&#xff0c;V.R.Pratt&#xff09;&#xff0…

常用的SQL语句

常用的SQL语句 一、基础 1、说明&#xff1a;创建数据库 CREATE DATABASE database-name 2、说明&#xff1a;删除数据库 drop database dbname 3、说明&#xff1a;备份sql server 1 --- 创建 备份数据的 device 2 3   USE master 4 5   EXEC sp_addumpdevice disk, te…

论文浅尝 | 用对抗学习做知识表示(NAACL2018)

论文链接&#xff1a;https://arxiv.org/pdf/1711.04071.pdf代码链接&#xff1a;https:// github.com/cai-lw/KBGAN 本文针对在生成负样本时有大部分负样本可以很好地和正样本区分开&#xff0c;对训练的影响不大&#xff0c;提出了使用生成对抗网络&#xff08;GANs&#xff…

为什么搜索与推荐场景用AUC评价模型好坏?

星标/置顶小屋&#xff0c;带你解锁最萌最前沿的NLP、搜索与推荐技术文 | 吴海波在互联网的排序业务中&#xff0c;比如搜索、推荐、广告等&#xff0c;AUC&#xff08;Area under the Curve of ROC&#xff09;是一个非常常见的评估指标。网上关于AUC的资料很多&#xff0c;知…

Redis系列教程(九):Redis的内存回收原理,及内存过期淘汰策略详解

Redis内存回收机制 Redis的内存回收主要围绕以下两个方面&#xff1a; 1.Redis过期策略:删除过期时间的key值 2.Redis淘汰策略:内存使用到达maxmemory上限时触发内存淘汰数据 Redis的过期策略和内存淘汰策略不是一件事&#xff0c;实际研发中不要弄混淆了&#xff0c;下面会…

字符串匹配算法(Trie树)

文章目录1. Trie树概念2. Trie树操作2.1 存储2.2 查找2.3 插入2.4 删除2.5 打印3. 完整代码4. Trie树与散列表、红黑树的比较4.1 思考题参考文章5. 练习题1. Trie树概念 Trie树&#xff0c;也叫字典树&#xff0c;它是一个树形结构。是一种专门处理字符串匹配的数据结构&#…

论文浅尝 | 基于知识图谱嵌入的 Bootstrapping 实体对齐方法

来源: IJCAI 2018链接: https://www.ijcai.org/proceedings/2018/0611.pdf本文关注基于知识图谱嵌入(后文全部简称为知识嵌入)的实体对齐工作&#xff0c;针对知识嵌入训练数据有限这一情况&#xff0c;作者提出一种 bootstrapping 策略&#xff0c;迭代标注出可能的实体对齐&a…

大规模领域词汇库项目DomainWordsDict:涵盖68个领域、共计916万的词汇库资源开放

项目概述 DomainWordsDict, Chinese words dict that contains more than 68 domains, which can be used as text classification、knowledge enhance task。涵盖68个领域、共计916万词的专业词典知识库&#xff0c;可用于文本分类、知识增强、领域词汇库扩充等自然语言处理应…

递归」与「动态规划

原文地址&#xff1a;https://juejin.im/post/5c2308abf265da615304ce41#heading-8 在学习「数据结构和算法」的过程中&#xff0c;因为人习惯了平铺直叙的思维方式&#xff0c;所以「递归」与「动态规划」这种带循环概念&#xff08;绕来绕去&#xff09;的往往是相对比较难以…

当知识图谱遇上推荐系统之DKN模型(论文笔记一)

Deep Knowledge-Aware Network for News Recommendation 类别&#xff1a;依次学习 首先使用知识图谱特征学习得到实体向量和关系向量&#xff0c;然后将这些低维向量引入推荐系统&#xff0c;学习得到用户向量和物品向量。 [论文下载链接]https://arxiv.org/abs/1801.08284…

POJ 1936 字符匹配(水题)

题目链接&#xff1a; http://poj.org/problem?id1936 题目大意&#xff1a; 给定字符a&#xff0c;b&#xff0c;问b中去掉一些字符后能不能得到a 解题思路&#xff1a; 暴力从前往后扫描一遍即可。 AC代码&#xff1a; /*** description: poj1936水题* author: michael…

领域应用 | 从数据到智慧,知识图谱如何推动金融更智能?

本文转载在公众号&#xff1a;恒生技术之眼。在《人工智能知识图谱&#xff1a;如何规整海量金融大数据&#xff1f;》一文中&#xff0c;笔者曾提到&#xff0c;面向人工智能的大数据治理&#xff0c;势必能有效支撑智能金融从感知智能向认知智能变革。这是因为目前在资本市场…

2021届秋招算法岗真的要灰飞烟灭了吗?

星标/置顶小屋&#xff0c;带你解锁最萌最前沿的NLP、搜索与推荐技术文 | 不拖更的夕小瑶2014年末入坑AI&#xff0c;一路见证了AI行业的快速起飞、爆炸、焦虑和冷却。小夕前几天在知乎上看到一个问题《如何看待2021年秋招算法岗灰飞烟灭》被顶上了热榜。有点感叹&#xff0c;怎…

万字长文:近年来学界、业界视角下的“事理图谱”发展总结与思考

一、引言 大部分技术都会经历从提出&#xff0c;到验证&#xff0c;再到修正&#xff0c;再到落地的这样一个过程。事理图谱这个概念从国内学者自2017年提出到现在&#xff0c;已经经历了近4年的时间&#xff0c;那么在这四年的时间里&#xff0c;事理图谱目前处于一个什么…