数据结构(邓俊辉)学习笔记】串 09——BM_BC算法:以终为始

news2024/11/22 14:14:04

文章目录

  • 1. 不对称性
  • 2. 善待教训
  • 3.前轻后重
  • 4.以终为始

1. 不对称性

上一节所介绍的 KMP 算法计算时间,在最坏情况下也可以保证不超过线性。这的确是一个好消息。然而,倘若我们因此就停下继续优化的脚步,那就大错特错了。

在这里插入图片描述

实际上,串匹配问题与一般的搜索问题的确有着本质的区别。在我们此前所讨论的所有搜索算法中,每次比对都是一种一对一的模式,也就是一个目标与另一个候选者判定二者是否相等,的确只需常数的时间。而现在,虽然基本的数据对象是单个的字符,而所谓的串匹配则是相对于由若干个字符在局部组成的一个片段而言的,也就是说是由多个字符对多个字符,二者匹配,当且仅当每一对字符彼此相等。

然而,需要特别注意的是,反过来,一旦发现有一对字符不等,我们就立即可以判断串失配。于此可见,从计算成本的角度来看,判定一对串是否相等与判定它们是否不等并不是完全一样。

我们接下来将要介绍的 BM 算法,就充分地利用了这一性质,从而使得它匹配的效率得以进一步地提高。

实际上,这一算法同时采用了两种策略,在接下来的这一节,我们首先来讨论所谓的坏字符策略。

2. 善待教训

在这里插入图片描述

让我们将视线拉回到蛮力算法。应该记得,蛮力算法一次典型地运行过程,可以表示为这样一幅图。是的,如果将文本串固定在这里(灰色),那么这些就是模式串在不同对齐位置处的历史快照。事实上,模式串在每一个对齐位置的故事,都是类似的。

  1. 首先,要经过若干次成功的比对,在这里,用绿色的线条来表示。
  2. 而接下来,总是一次失败的比对。
  3. 之后,再下一位置的故事依然。该算法也需要经过若干次成功的匹配,然后终止于失败地比对。

而在接下来的第三次、第四次、第五次等等,各次对齐中,也同样都是要经过若干次成功的比对,并终止于一次失败的比对。因此,就局部的每一次对齐而言,我们都需要经过若干次的成功,并终止于一次失败。

请注意,在每次对齐中,尽管只有一次失败,但却足以确定相应的对齐位置是无效的。事实上,只有最后一次对齐位置才有可能是成功的。因此,就整体而言,恰好与刚才局部的模式相反。多次失败,以及至多一次的成功。

因此,如果我们着眼于改进蛮力算法。那么,我们的目标与其说是要加速匹配,不如说是要加速失败,或更准确地讲,要尽可能快速地、低成本地排除掉无效的对齐位置。

事实上,KMP 等算法就是这样。应该记得, KMP 会聪明地排除掉其中的若干个,甚至是大量的对齐位置,从而大大的节省计算的成本。甚至从某种意义上讲,我们或许能够做得比 KMP 还要出色。

正如我们刚才所指出的,就排除某个对齐位置而言,相应的那些成功的比对并不重要,而在这个意义上起实质作用的,反而是那些失败的比对。 如果真的像刚才说的那样,我们总是需要在排除多个无效的对齐位置之后,才能够确定最终有效的对齐位置,那么我们反倒应该更加期盼这种失败的比对出现得更早。

比如,按照这种思路的一种极端情况就是,我或许可能只做这些失败的比对,就足以排除掉相应的对齐位置。为了排除掉一个对齐位置,我们平均只需做常数次比对,而且在理想平均的情况下,甚至不必去关心,哪个字符更有可能首先失败,而只需按部就班地从前向后,自左而右地依次比对就可以了。

是的,如果我们的关注力只停留于单个的比对位置,那么固然如此。但是,倘若我们将所有的对齐位置作为一个整体来考虑,我们就会发现,实际上在每一个对齐位置处,按照什么样的次序去尝试对比各自符却十分敏感而关键。我们的建议是,你或许应该优先去比对那些靠后、乃至最靠后的字符。

也就是说,与我们常规的做法恰恰相反。或许你应该更多地去关注教训。是的,如果教训的确不能避免的话,或许我们应该让它更早地暴露出来,或者再准确地讲,应该让更大的教训更早的暴露出来。

那么,为什么就串匹配算法而言,在越靠后的位置所获得的教训所具有的价值越高呢?

3.前轻后重

在这里插入图片描述

依然来考察蛮力算法的一次典型执行过程。假定在依次排除了一系列的对齐位置之后,抵达了下一个对齐位置。此时我们有两种策略,或者优先去比对靠前的字符,或者反过来优先比对靠后的字符。

这里我们再次强调一下,只要字母表的规模不是很小,那么就每一次字符对字数的比对而言,成功的概率将远远小于失败的概率,也就是说我们更有可能获得教训,而不是经验。

果真如此,在前后这两个位置所获得的教训,其价值大小又有何区别呢?的确有所区别,而且很大,其背后的原因在于,根据这样的每一次教训,我们不仅可以排除一个对齐位置,而且可能排除掉多个对齐位置。

比如,根据在前一位置所获得的教训,我们或者可以同时排除掉三个对齐位置,而不是一个。类似地,在后一位置所获得教训,则有可能帮助我们排除掉更多的对齐位置,远远更多的对齐位置。

是的,如果就这类教训对我们提高计算效率的意义而言,的确呈现出前轻后重的特点。

4.以终为始

在这里插入图片描述
既然按照以上的分析,模式中越靠后的字符对算法性能的优化作用更大,那么或许我们应该把计算的方向和次序颠倒过来,以终为始。此话怎讲呢?

具体来说,在每一趟扫描中,我们都需要从末字符而不是首字符开始比对,也就是说扫描的方向将变成自后向前,而不再是自左向右。

来看这样一个具体的例子:这就是由12个字符所构成的一个文本串,而待匹配的模式串则由这三个字符组成。

  1. 为了启动第一趟扫描,我们首先要将它们的首字符彼此对齐。然而这里,我们将首先从末字符开始比对。可以看到,这是一次失败的比对。

    实际上,这种失败的比对必然是更容易发生的,因为你注意到,我们在这一所采用的字母表是由汉字构成的,即便是只记录常用的汉字,其规模也至少超过5,000,从多达5,000个的候选中任取2个,二者相同的概率自然是非常之低的。反过来不同的概率也就应该很高。

    所以对于这次失败,我们应该有足够的心理准备。因此接下来,或许我们应该静下心来,对这次失败好好地作一分析。或许你能从中悟出点什么。是的,表面上看这里是"名"与"道"之间的冲突,而实际上我们可以将这次教训进一步地概括为,如果需要在"道"的附近实现一次完整的匹配,那么至少与它对应的那个字符就应该也是"道",而非"名"。

  2. 现在,按照这一必要条件来反观我们的模式串,就会发现其实"道"根本就没有出现在其中,这一点非常重要,如果能够悟到这一点,我们就可以大胆地将这个模式串整体的移过这个位置,从而在这样一个新的位置对齐。

  3. 接下来的这趟比对与上一趟基本类似,依然是"名"与"道"不符。所以再一次地,我们同样可以将整个字符串移过这个位置。从而在这样一个新的位置继续对齐。

  4. 接下来的这趟扫描,依然起始于末字符。可以看到这是一次成功的比对,应该觉得非常幸运,因为正如刚才所言,这种成功比对出现的概率是极低的。于是接下来,我们再去进而比对与之在左侧相邻的那一对字符。这依然是一次大概率的失败比对,然而作为一次新的教训,它同样可能让我们悟到点什么。

    是的,依然是这个失配位置处的"可"字。它同样给出了一个能够局部完全匹配的必要条件。然而,如果按照这个必要条件,反观我们的模式串,就会发现其中同样没有出现这个字符。

  5. 因此接下来,我们可以将这个字符串同样整体的移过这个失配的位置,并在这样一个新的位置继续对齐,然后依然从末字符开始继续比对,同样,依然是大概率的失败。

    而此后,只要我们能够静下心来对这一教训作一分析,就同样能够针对完全匹配给出一个必要条件。我想,你已经看出这个必要条件来了。是的,如果能够在包括这个"常"字在内的局部,实现一次完全匹配,那么模式串中与之对齐的,就同样也应该是一个"常"字。

    这一回,我们依次反观模式串,就会发现的确存在一个"常"字。因此接下来,我们就不妨将整个字符串向右移动一个单位,从而让"常"字的确与"常"字相对。在满足的这样一个必要条件之后,我就可以来开始新一轮比对。

  6. 首先是末字符,这是成功的。而且接下来的各次比对也都是成功的。这意味着我们终于发现了一次完全的匹配,整个算法也就可以随即终止。

现在,我们来核算一下这个算法所花费的计算成本,也就是在期间所做的比对次数。按照我们这里标注的习惯,深色的都是成功的比对,灰色的则是失败的比对。而白色的,则是没有进行,从而节省下来的比对。

可以看到,我们累计做了4次成功的比对,以及4次失败的比对。没错,4次加4次,累计不过8次,这一结果意义非凡。因为我们注意到,即便忽略掉模式串,仅文本串也至少有12个字符,而我们在算法过程中所执行的比对次数居然要小于这一长度。换而言之,平均在每个字符上所消耗的比对居然不足一次。

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

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

相关文章

如何在Java爬虫中设置代理IP:详解与技巧

在进行网络爬虫时,使用代理IP可以有效地避免被目标网站封禁,提升数据抓取的成功率。本文将详细介绍如何在Java爬虫中设置代理IP,并提供一些实用的技巧和示例代码。 为什么需要代理IP? 在进行爬虫操作时,频繁的请求可能…

深度学习基础—彩色图片的卷积运算

深度学习基础—卷积运算http://t.csdnimg.cn/2mRei 上篇文章卷积运算实际是灰度图像的运算(2维空间),但是实际中我们彩色图片使用的更多,和灰度图像不同的是:彩色图片是由三原色(红、绿、蓝)组成…

C# 对桌面快捷方式的操作设置开机启动项

首先在项目中引入Windows Script Host Object Model,引入方式如下图。 对于桌面快捷方式的修改无非就是将现有的快捷方式修改和添加新的快捷方式。 1、遍历桌面快捷方式,代码如下。 string desktopPath Environment.GetFolderPath(Environment.Special…

机器学习:DBSCAN算法(内有精彩动图)

目录 前言 一、DBSCAN算法 1.动图展示(图片转载自网络) 2.步骤详解 3.参数配置 二、代码实现 1.完整代码 2.代码详解 1.导入数据 2.通过循环确定参数最佳值 总结 前言 DBSCAN(Density-Based Spatial Clustering of Applications w…

World of Warcraft [CLASSIC][80][Grandel] Call to Arms: Strand of the Ancients

Call to Arms: Strand of the Ancients - Quest - 魔兽世界怀旧服CTM4.34《大地的裂变》数据库_大灾变85级魔兽数据库_ctm数据库 Call to Arms: Strand of the Ancients 战斗的召唤:远古海滩 打掉最后一个门【古代圣物之厅】,人跳进去就赢了

算法之二分查找法和双指针

用二分查找法刷leetcode算法题目的时候&#xff0c;经常遇到视频看着理解很透彻&#xff0c;当上手写时一看就会&#xff0c;一写就废。二分查找法涉及边界条件很多&#xff0c;逻辑很简单&#xff0c;就是写不好。何时写 while(left<right)&#xff0c;while(left<right…

【动态规划】背包问题 - 二维费用的01背包问题

文章目录 1. 前言2. 二位费用的01背包问题2.1_一和零2.2_盈利计划2.3_珠宝的最高价值 3. 似包非包问题3.1_不同的二叉搜索树3.2_组合总和Ⅳ 1. 前言 关于 动态规划的理解 与例题&#xff0c;点击&#x1f447; 【动态规划】C解决斐波那契模型题目&#xff08;三步问题、爬楼梯…

winXP下构建python开发环境

近期车间有个动平衡检测仪数采的需求&#xff0c;工控机是xp系统&#xff0c;原理也很简单&#xff0c;监控文件变化&#xff0c;发现有新的检测数据就调用远程接口传输到服务器上去。 通常python监控文件变化会用watchdog这个库&#xff0c; 可是xp太老了&#xff0c;测试了一…

身份实名认证-身份证实名认证-身份证实名-实名认证-身份证二要素-身份证实名认证-身份实名认证-身份证号码实名认证核验校验接口

身份证号码实名认证接口API是一种服务&#xff0c;它允许开发者或企业通过编程方式验证用户提供的身份证号码是否真实有效&#xff0c;以及该身份证号码与提供者的姓名是否匹配。这种服务对于确保用户身份的真实性、防止欺诈行为以及遵守相关法律法规&#xff08;如反洗钱法、网…

自博弈-PSRO类方法综述

参考文章&#xff1a;PSRO2024最新综述 关键名词 解释 Meta-Strategy Solver (MSS) 元博弈求解器&#xff0c;从现有策略集合中提取meta-strategy&#xff08;策略集合中每个策略对应一个权重&#xff09;用于构造新策略的优化目标 Response Objective&#xff08;RO&#…

【系统分析师】-缓存

目录 1、常见分类 2、集群切片方式 3、Redis 3.1、分布式存储方式 3.2、数据分片方式 3.3、数据类型 3.4、持久化方案 3.5、内存淘汰机制 3.6、Redis常见问题 4、布隆过滤器 1、常见分类 1、MemCache Memcache是一个高性能的分布式的内存对象缓存系统&#xff0c;用…

RocketMQ:高速消息中间件的秘密武器

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 关于RocketMQ的详细图表&#xff0c;包含了Producer、Consumer、Broker和NameServer等关键组件&#xff0c;展示…

你知道有哪些Spring MVC扩展点可以解析接口参数和处理返回值吗?

1.概述 Spring MVC 是一个灵活且强大的框架&#xff0c;它允许开发者在框架的基础上进行深度定制&#xff0c;以满足各种复杂的业务需求。HandlerMethodArgumentResolver 和 HandlerMethodReturnValueHandler 是 Spring MVC 提供的两个重要扩展点&#xff0c;分别用于处理控制…

SLF4J 警告 - SLF4J: Class path contains multiple SLF4J bindings.

SLF4J 警告是因为类路径中存在多个 SLF4J 绑定。SLF4J 是一个抽象的日志接口&#xff0c;它可以与不同的日志实现&#xff08;如 Logback 或 SLF4J Simple&#xff09;一起使用。这个警告表明在你的项目中&#xff0c;SLF4J 找到了多个实现&#xff0c;导致它不知道该使用哪一个…

python如何判断回文

打开JUPTER NOTEBOOK&#xff0c;新建一个PYTHON文档。 n input("Please input string: ") print(n) 我们首先让用户输入要进行判断的字符串&#xff0c;然后打印出来查看一下。 n input("Please input string: ") is_palidrome n[::-1] if n is_palid…

Windows IPv6漏洞CVE-2024-38063

2024年8月&#xff0c;微软发现Windows10、Windows11、Windows Server2008~Server2022系统里&#xff0c;有个TCP/IP栈的远程代码执行漏洞&#xff0c;它通过目标系统的445端口&#xff0c;走IPv6协议&#xff0c;向目标系统发生特制的TCP包&#xff0c;执行任意代码&#xff0…

MySQL索引(三)

MySQL索引(三) 文章目录 MySQL索引(三)为什么建索引&#xff1f;怎么建立索引为什么不是说索引越多越好什么时候不用索引更好 索引怎么优化索引失效如何解决索引失效 学习网站&#xff1a;https://xiaolincoding.com/ 为什么建索引&#xff1f; 1.索引大大减少了MySQL需要扫描…

io进程中进程的创建,回收,退出

目录 一丶什么是进程 1.概念 2.特点 3 进程段 4.进程分类 5.进程状态 6.进程状态切换图 7.进程相关命令 8.优先级调度 二丶进程函数接口 1.创建进程fork() 2.进程回收wait() 3.结束进程exit() 4.获取进程号getpid(),getppid() 5.exec函数族 6.守护进程 特点&a…

AI辅助创作全攻略:如何高效利用人工智能撰写各类作品文字

在数字化时代的浪潮中人工智能&#xff08;AI&#xff09;已经渗透到咱们生活的方方面面&#xff0c;其中就包含文学创作领域。辅助创作不仅可以加强写作效率还能激发创作灵感宽创作视野。 那么怎么样高效利用人工智能撰写各类作品文字呢&#xff1f;本文将为您详细解析这一全攻…

软件测试 | 测试用例

测试用例&#xff08;Test Case&#xff09;是为了实施测试而向被测试的系统提供的一组集合&#xff0c;这组集合包含&#xff1a;测试环境&#xff0c;测试步骤&#xff0c;测试数据&#xff0c;预期结果等要素。 设计测试用例原则⼀&#xff1a; 测试用例中⼀个必需部分是对…