数据结构(邓俊辉)学习笔记】串 12——BM_BC算法:性能分析

2024-08-30 11:20

本文主要是介绍数据结构(邓俊辉)学习笔记】串 12——BM_BC算法:性能分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 1. 最好情况
  • 2. 最坏情况

1. 最好情况

在这里插入图片描述

以下,就让我们从最好情况的角度,来考察坏字符策略的性能。实际上,在最好情况下的性能之好,要远远超过我们的想象。具体来说,此时的时间成本可以度量为 O(n/m)。没错,除法。你能构造出这样的一个具体实例吗?这就是一个:

可以看到,模式串完全由0组成,而文本串呢?则由与模式串等长的若干个片段依次拼接而成。在每一个片段中,末字符都不是0,比如说取作1,而其余的字符实质上都无所谓,因此笼统的记作 x。我不妨就此例来走一遍 BM 算法。

  1. 第一次对齐的位置在这里,而首次比对就是0/1的失配。在这里,0就是坏字符,而且在模式串中,根本就找不到足以与 1 匹配的字符。
  2. 于是,BM法将会把模式串整体的移过这个失配的位置,并使之与文本串中的下一个片段彼此对齐。而接下来的故事,与刚才的那个片段完全一样。首先末字布 0 无法与 1 匹配,而且其余的 0 也不能与之匹配。
  3. 因此,算法将再次整体的后移模式串。以致再次、再再次、持续地整体移动下去。

纵观整个过程,每经过常数次的比对,我们就可以向后整体的移动 m 个字符。既然文本串累计不过 n 个字符,因此在这种情况下,算法至多会移动 n/m 次。

由此可见,在这种情况下,算法的确只需要运行这么多的时间,即便是相对于 KMP 而言,这也是一个极大的提高,更不用说蛮力算法了。这个例子并不失一般性,实际上只要 P 中的字符都是坏字符,那么每次我们都可以整体地对它进行移动,也就是说我们只需要常数次的比较,就可以整体地排除掉 m 的对齐位置。

由此可见,如果说 KMP 算法是个利用经验的高手,那么 BM 算法则是非常善于借鉴教训的高手。从这个意义上讲,BM 算法更加欢迎失败比对的出现。那么在什么情况下更容易出现失败的比对呢?

也就是说,对于任何一对随机出现的字符所做的比对,失配的概率在什么情况下更小呢?在这里我们再次回到那样一个重要的指标,也就是字母表的规模。实际上字母表的规模越大,单次匹配成功的概率也就越小,失配的概率也就越大,从而 BM 算法的优势也就更为明显

比如在处理汉字甚至 unicode 编码时,BM算法就是再适合不过的了。

2. 最坏情况

然而另一方面很遗憾,BM算法在最差情况下的性能也的确非常差。

具体来说,它的算法复杂度有可能会退化到蛮力算法的水平,也就是 O(n*m)。就此,你能举出一个具体的实例吗?我可以给你一点提示,你不妨去参考一下蛮力算法最坏情况的那个实例。
在这里插入图片描述
是的,我想你已经想到了,就是这个。这一次文本串倒是完全由0组成的,而模式串也几乎是由 0 组成的,唯一的例外是它的首字符。按照 BM 算法的流程,我们首先需要从末字符开始进行比对,而且我们会经历一系列的成功,直到最后一步才失败。可以看到,在这次失败之前,我们已经花费了 O(m) 的成本。

然而最糟糕的还不是这个,因为我们花费了如此之高的成本所换来的那个教训,居然对我们不会有太多的帮助。因为此时正属于我们此前所讲的那种最为特殊的情况,也就是说坏字符的替代者应该是 0。但是在模式上中,最后出现的那个 0 过于靠后,以至于如果我们需要将它与此前的适配字符对齐,将导致模式串的左移而不是右移。因此在这种情况下,我们只好搬出那个假想的通配哨兵,并用那通配的哨兵与这个失配的字符相对齐。

非常可惜,这样的效果只相当于模式串向右移动一个单位。没错,我们每花费 O(m) 的成本,换来的收获只是向后移动了一步,而总体共有 n。因此在这种情况下的总体计算成本的确应该是 n 与 m 的乘积。
在这里插入图片描述

我们不妨来反思一下, BM 算法当前的这个版本为何还有可能会出现如此之差的情况呢?没错,经验。我们已经看到 BM 算法目前的这个版本的确已经能够很好地借鉴教训。也正因为此,它才能够在最好情况下有出色的表现。然而,很遗憾,到目前为止,它还没能够有效地利用好经验。具体来说,也就是此前的那些成功比对所提供的有益信息。实际上,完整的 BM 算法的确也可以同时很好地利用这方面的信息。

因此,我们需要给 BM 算法增加一个策略,也就是所谓的好后缀策略。这个策略再加上我们刚刚介绍的坏字符策略,就可以使得 BM 算法变得几乎完美。

这篇关于数据结构(邓俊辉)学习笔记】串 12——BM_BC算法:性能分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

Tomcat高效部署与性能优化方式

《Tomcat高效部署与性能优化方式》本文介绍了如何高效部署Tomcat并进行性能优化,以确保Web应用的稳定运行和高效响应,高效部署包括环境准备、安装Tomcat、配置Tomcat、部署应用和启动T... 目录Tomcat高效部署与性能优化一、引言二、Tomcat高效部署三、Tomcat性能优化总结Tom

C#使用DeepSeek API实现自然语言处理,文本分类和情感分析

《C#使用DeepSeekAPI实现自然语言处理,文本分类和情感分析》在C#中使用DeepSeekAPI可以实现多种功能,例如自然语言处理、文本分类、情感分析等,本文主要为大家介绍了具体实现步骤,... 目录准备工作文本生成文本分类问答系统代码生成翻译功能文本摘要文本校对图像描述生成总结在C#中使用Deep

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制