1. BM算法简介:
KMP算法其实并不是效率最高的字符串匹配算法,实际应用的并不多,各种文本编辑器的“查找”功能大多采用的是BM算法(Boyer Moore)。BM算法效率更高,更容易理解。
2. BM算法分析:
(1)假定字符串为"HERE IS A SIMPLE EXAMPLE",搜索词为"EXAMPLE"。

(2) 首先,"字符串"与"搜索词"头部对齐,从尾部开始比较。这是一个很聪明的想法,因为如果尾部字符不匹配,那么只要一次比较,就可以知道前7个字符(整体上)肯定不是要找的结果。我们看到,"S"与"E"不匹配。这时,"S"就被称为"坏字符"(bad
character),即不匹配的字符。我们还发现,"S"不包含在搜索词"EXAMPLE"之中,这意味着可以把搜索词直接移到"S"的后一位。

(3)依然从尾部开始比较,发现"P"与"E"不匹配,所以"P"是"坏字符"。但是,"P"包含在搜索词"EXAMPLE"之中。所以,将搜索词后移两位,两个"P"对齐。

(4)我们由此总结出"坏字符规则":
后移位数 = 坏字符的位置 - 搜索词中的上一次出现位置值
如果"坏字符"不包含在搜索词之中,则上一次出现位置为 -1。
以"P"为例,它作为"坏字符",出现在搜索词的第6位(从0开始编号),在搜索词中的上一次出现位置为4,所以后移 6 - 4 = 2位。再以前面第二步的"S"为例,它出现在第6位,上一次出现位置是 -1(即未出现),则整个搜索词后移 6 - (-1) = 7位。

(5)依然从尾部开始比较,"E"与"E"匹配。

(6)比较前面一位,"LE"与"LE"匹配。

(7)比较前面一位,"PLE"与"PLE"匹配。

(8)比较前面一位,"MPLE"与"MPLE"匹配。我们把这种情况称为"好后缀"(good
suffix),即所有尾部匹配的字符串。注意,"MPLE"、"PLE"、"LE"、"E"都是好后缀。

(9)比较前一位,发现"I"与"A"不匹配。所以,"I"是"坏字符"。

(10)根据"坏字符规则",此时搜索词应该后移
2 - (-1)= 3 位。问题是,此时有没有更好的移法?

(11)我们知道,此时存在"好后缀"。所以,可以采用"好后缀规则":
后移位数 = 好后缀的位置 - 搜索词中的上一次出现位置
举例来说,如果字符串"ABCDAB"的后一个"AB"是"好后缀"。那么它的位置是5(从0开始计算,取最后的"B"的值),在"搜索词中的上一次出现位置"是1(第一个"B"的位置),所以后移 5 - 1 = 4位,前一个"AB"移到后一个"AB"的位置。
再举一个例子,如果字符串"ABCDEF"的"EF"是好后缀,则"EF"的位置是5 ,上一次出现的位置是 -1(即未出现),所以后移 5 - (-1) = 6位,即整个字符串移到"F"的后一位。这个规则回到上文的这个例子。此时,所有的"好后缀"(MPLE、PLE、LE、E)之中,只有"E"在"EXAMPLE"还出现在头部,所以后移
6 - 0 = 6位。有三个注意点:
(1)"好后缀"的位置以最后一个字符为准。假定"ABCDEF"的"EF"是好后缀,则它的位置以"F"为准,即5(从0开始计算)。
(2)如果"好后缀"在搜索词中只出现一次,则它的上一次出现位置为 -1。比如,"EF"在"ABCDEF"之中只出现一次,则它的上一次出现位置为-1(即未出现)。
(3)如果"好后缀"有多个,则除了最长的那个"好后缀",其他"好后缀"的上一次出现位置必须在头部。比如,假定"BABCDAB"的"好后缀"是"DAB"、"AB"、"B",请问这时"好后缀"的上一次出现位置是什么?回答是,此时采用的好后缀是"B",它的上一次出现位置是头部,即第0位。这个规则也可以这样表达:如果最长的那个"好后缀"只出现一次,则可以把搜索词改写成如下形式进行位置计算"(DA)BABCDAB",即虚拟加入最前面的"DA"。

回到上文的这个例子。此时,所有的"好后缀"(MPLE、PLE、LE、E)之中,只有"E"在"EXAMPLE"还出现在头部,所以后移
6 - 0 = 6位。
(12) 可以看到,"坏字符规则"只能移3位,"好后缀规则"可以移6位。所以,Boyer-Moore算法的基本思想是,每次后移这两个规则之中的较大值。更巧妙的是,这两个规则的移动位数,只与搜索词有关,与原字符串无关。因此,可以预先计算生成《坏字符规则表》和《好后缀规则表》。使用时,只要查表比较一下就可以了。

(13)继续从尾部开始比较,"P"与"E"不匹配,因此"P"是"坏字符"。根据"坏字符规则",后移
6 - 4 = 2位。

(14)从尾部开始逐位比较,发现全部匹配,于是搜索结束。如果还要继续查找(即找出全部匹配),则根据"好后缀规则",后移
6 - 0 = 6位,即头部的"E"移到尾部的"E"的位置。

3. BM算法Java实现:
public class BM {
final static int CARD_CHAR_SET = 256;// 字符集规模
/*
* @param mainStr 主串
* @param subStr 模式串
*/
public static int getMatchIndex(String mainStr, String subStr) {
int[] BC = BuildBC(subStr); // 坏字符表
int[] GS = BuildGS(subStr); // 好后缀表
// 查找匹配
int i = 0; // 模式串相对于主串的起始位置(初始时与主串左对齐)
while (mainStr.length() - subStr.length() >= i) { // 在到达最右端前,不断右移模式串
int j = subStr.length() - 1; // 从模式串最末尾的字符开始
while (subStr.charAt(j) == mainStr.charAt(i + j))
if (0 > --j) // 自右向左比较
break;
if (0 > j) // 若最大匹配后缀 == 整个模式串(说明已经完全匹配)
break;
else
i += MAX(GS[j], j - BC[mainStr.charAt(i + j)]);// 在位移量BC和GS之间选择大者,相应地移动模式串
}
return (i);
}
/*
* 构造Bad Charactor Shift表BC[] - 坏字符表
*/
protected static int[] BuildBC(String subStr) {
int[] BC = new int[CARD_CHAR_SET]; // 初始化坏字符表
int j;
for (j = 0; j < CARD_CHAR_SET; j++)
BC[j] = -1; // 首先假设该字符没有在P中出现
for (j = 0; j < subStr.length(); j++) // 自左向右迭代:更新各字符的BC[]值
BC[subStr.charAt(j)] = j;
return (BC);
}
/*
* 构造Good Suffix Shift表GS[] - 好后缀表
*/
protected static int[] BuildGS(String subStr) {
int m = subStr.length();
int[] SS = ComputeSuffixSize(subStr); // 计算各字符对应的最长匹配后缀长度
int[] GS = new int[m]; // Good Suffix Index
int j;
for (j = 0; j < m; j++)
GS[j] = m;
int i = 0;
for (j = m - 1; j >= -1; j--)
if (-1 == j || j + 1 == SS[j]) // 若定义SS[-1] = 0,则可统一为:if (j+1 == SS[j])
for (; i < m - j - 1; i++)
if (GS[i] == m)
GS[i] = m - j - 1;
for (j = 0; j < m - 1; j++)
GS[m - SS[j] - 1] = m - j - 1;
return (GS);
}
/*
* 计算P的各前缀与P的各后缀的最大匹配长度
*/
protected static int[] ComputeSuffixSize(String subStr) {
int m = subStr.length();
int[] SS = new int[m];// Suffix Size Table
int s, t; // 子串P[s+1, ..., t]与后缀P[m+s-t, ..., m-1]匹配
int j; // 当前字符的位置
SS[m - 1] = m; // 对最后一个字符而言,与之匹配的最长后缀就是整个P串
s = m - 1; // 从倒数第二个字符起,自右向左扫描P,依次计算出SS[]其余各项
t = m - 2;
for (j = m - 2; j >= 0; j--) {
if ((j > s) && (j - s > SS[(m - 1 - t) + j]))
SS[j] = SS[(m - 1 - t) + j];
else {
t = j; // 与后缀匹配之子串的终点,就是当前字符
s = MIN(s, j); // 与后缀匹配之子串的起点
while ((0 <= s) && (subStr.charAt(s) == subStr.charAt((m - 1 - t) + s)))
s--;
SS[j] = t - s;// 与后缀匹配之最长子串的长度
}
}
return (SS);
}
protected static int MAX(int a, int b) {
return (a > b) ? a : b;
}
protected static int MIN(int a, int b) {
return (a < b) ? a : b;
}
// 测试类
public static void main(String[] args) {
String mainStr = "HERE IS A SIMPLE EXAMPLE";
String subStr = "EXAMPLE";
System.out.println("字符串匹配的位置为: " + getMatchIndex(mainStr, subStr));
}
}
4. BF,KMP,BM算法比较:
(1) BF,KMP算法移动模式串的时候是从左到右,进行比较的时候也是是从左到右的。BM算法移动模式串的时候是从左到右,而进行比较的时候是从右到左的。
(2) 算法效率: BM算法 > KMP算法 > BF算法
5. BM算法参考资料:
(1) 阮一峰的博客: 字符串匹配的Boyer-Moore算法
(2) 淘宝搜索技术博客:字符串匹配的那些事
分享到:
相关推荐
双目摄像头python测试代码
VS2010旗舰版的VB.NET版本任意进制互相转换程序源代码QZQ
前端开发_Mantine框架_Vite模板_快速启动_开发指_1744170109.zip
内容概要:本文详细介绍了RK3568核心板的设计要点,涵盖硬件和软件两大部分。硬件方面,着重讨论了电源设计、时钟设计、DDR布线、散热设计等关键环节,提供了具体的实例代码和调试技巧。软件部分则涉及驱动开发、系统移植等内容,展示了如何通过设备树文件配置和驱动程序编写来实现硬件与操作系统的无缝对接。此外,还分享了许多实用的调试经验和优化方法,帮助开发者更好地理解和掌握RK3568核心板的设计。 适合人群:从事嵌入式系统开发的技术人员,尤其是对RK3568核心板感兴趣的硬件工程师和软件开发者。 使用场景及目标:适用于准备开发基于RK3568核心板的产品的研发团队,旨在帮助他们快速上手并解决实际开发过程中遇到的问题,提高开发效率和产品质量。 其他说明:文中不仅提供了理论指导,还包括大量实战案例和代码片段,便于读者理解和实践。同时,针对一些常见的错误和难题给出了有效的解决方案,有助于减少开发周期和技术风险。
前端开发_Vite打包工具_Vue2框架_TypeScrip_1744171829.zip
试题:矩阵理论:运算与性质.docx
内容概要:本文档为第八届蓝桥杯嵌入式设计与开发项目省赛的基础知识试题部分,主要考察参赛者对嵌入式系统的基本概念、逻辑运算、微控制器特性、通信接口及时钟源的选择等知识点的理解。试题涵盖逻辑表达式的化简、门电路的功能识别、STM32F103RBT6微控制器的内核及数据类型支持情况、RS232接口用于双机通信所需的最少信号线数量、STM32程序下载调试的方式选择、可菊链连接的接口类型、USB外设开发的时钟源选择、DMA的工作机制以及简单电路的电压计算等多个方面,旨在全面检验选手的专业知识掌握程度。 适合人群:具有一定的电子技术与单片机开发基础,准备参加嵌入式设计与开发竞赛的学生或爱好者。 使用场景及目标:①帮助参赛者熟悉并巩固嵌入式系统相关理论知识;②作为赛前复习资料,提高解题速度和准确性;③通过练习加深对各种硬件特性和应用场景的理解。 其他说明:文档提供了详细的答案解析,有助于学习者更好地理解题目背后的原理,建议结合实际项目经验进行学习,同时注意不同版本器件之间的差异。
MX Player V1.90.1.apk
3dmax插件
内容概要:本文详细介绍了一个基于MATLAB的时间序列预测系统,特别是ARIMA模型的应用。首先介绍了如何从Excel文件中读取数据并进行预处理,如差分处理和平稳性检验。接着讨论了模型定阶方法,包括使用ACF/PACF图和BIC准则自动选择最佳模型阶数。然后讲解了模型参数估计、残差诊断以及预测阶段的具体步骤,包括反向差分和置信区间的计算。最后提供了动态可视化的实现方法,并分享了一些实用技巧和注意事项。 适合人群:具有一定MATLAB编程基础的研究人员、数据分析员、学生等。 使用场景及目标:适用于零售销量、电力负荷、交通流量等多种时间序列预测任务,帮助用户快速构建高效的时间序列预测模型,提高预测精度。 其他说明:文中还提到一些常见的陷阱和解决方案,如数据预处理中的时间戳处理、异常值处理等。同时提供了一些扩展功能,如季节性调整和GARCH模型的应用。
数据库管理_VitessOperator_兼容性支持_云端部_1744166554.zip
内容概要:本文详细介绍了将粒子群算法(Particle Swarm Optimization, PSO)与遗传算法(Genetic Algorithm, GA)相结合的方法来求解旅行商问题(Traveling Salesman Problem, TSP)。首先,文中展示了如何准备输入数据,即城市坐标存储于文本文件中并通过Python脚本加载。接着,重点讲解了混合算法的核心设计,包括粒子类定义、适应度计算、交叉操作以及变异操作的具体实现方式。此外,还讨论了算法的整体流程及其参数设置,并分享了一些实用的经验和技巧,如引入模拟退火思想以增强跳出局部最优的能力。 适合人群:对智能优化算法感兴趣的科研人员、学生或者有一定编程基础的数据科学家。 使用场景及目标:适用于需要高效求解TSP或其他类似组合优化问题的研究项目或应用系统开发。主要目标是在较短时间内获得接近最优解的路径安排。 其他说明:文中提供了完整的Python代码示例,便于读者理解和实践。同时也提到了一些性能优化措施,例如预处理距离矩阵、调整交叉率和变异率等方法来提升算法效率。
内容概要:本文详细介绍了LS-DYNA软件中霍普金森压杆(SHPB)动态劈裂仿真的源代码k文件的具体实现方法和优化技巧。首先概述了SHPB动态劈裂实验的基本原理,然后深入剖析了k文件中各部分的关键字和参数设置,如模型定义、材料属性、边界条件、接触定义、加载波形以及结果输出控制。文中还特别强调了常见错误和注意事项,并提供了具体的代码片段作为实例。此外,提到了‘LS-DYNA-浩雨’这一资源平台,分享了许多实用的经验和技术诀窍,有助于提高模拟精度和效率。 适合人群:从事材料动态力学性能研究的科研人员、工程技术人员以及对LS-DYNA仿真感兴趣的学者。 使用场景及目标:适用于需要进行SHPB动态劈裂仿真的研究人员,旨在帮助他们更好地理解和掌握LS-DYNA中k文件的编写规则和优化方法,从而提升仿真的准确性和可靠性。 其他说明:文章不仅提供了理论指导,还包括大量实践经验,能够帮助读者快速入门并解决实际问题。同时提醒读者注意一些常见的陷阱,避免不必要的错误。
3dmax插件
内容概要:本文详细介绍了工业自动化领域中同步磁阻电机及其驱动器、光伏储能双向变流器、EtherCAT伺服控制以及软启动器等关键技术的C语言实现方法和优化技巧。针对同步磁阻电机,文中探讨了六步换相算法、模型预测控制、磁链观测器等技术的应用;对于光伏储能,讨论了二阶广义积分器PLL、双向储能模式切换等问题;EtherCAT伺服方面则涉及主站协议栈的状态机设计及时钟同步;软启动器部分讲述了突加负载检测、电压斜坡控制等内容。每个技术点均附有具体的C代码示例,展示了如何应对实际工程中的挑战,如提高能效比、增强控制精度、确保协议兼容性等。 适合人群:从事工业自动化相关工作的工程师和技术人员,尤其是那些希望深入了解同步磁阻电机、光伏储能、EtherCAT伺服控制等领域核心技术实现的人群。 使用场景及目标:适用于正在参与或计划开展工业自动化项目的技术团队,旨在帮助他们掌握高效可靠的嵌入式控制系统开发技能,提升产品的性能指标,降低成本风险。同时,也为高校师生提供了宝贵的实践教学资料。 其他说明:文章不仅分享了理论知识,还结合大量真实案例,强调了实际应用中的注意事项和常见错误规避方法。通过对这些技术细节的学习,读者能够更好地理解和解决工业现场遇到的各种难题。
内容概要:本文详细介绍了多相(五相和六相)永磁同步电机的各种控制算法及其应用技巧。首先讨论了PI控制的基本原理和常见问题,如积分饱和。接着深入探讨了滑模控制的抖颤抑制技术和ADRC控制中的扩张状态观测器的应用。此外,文章还讲解了SVPWM矢量控制的具体实现方法,以及无位置传感器控制中扩展卡尔曼滤波的应用。针对五相和六相电机的特点,文中提供了具体的矢量选择策略和改进的滑模观测器算法。最后,文章分享了一些实用的经验和技术细节,如参数整定、谐波抑制和电流跟踪优化。 适合人群:具备一定电机控制基础知识的研发人员、工程师和研究人员。 使用场景及目标:适用于需要深入了解多相永磁同步电机控制算法的设计和优化,特别是在高性能驱动系统、工业自动化等领域。目标是提高系统的稳定性和效率,减少电流谐波和转矩波动。 其他说明:文章不仅提供了理论分析,还附带了大量的Matlab/Simulink代码片段,便于读者理解和实践。建议读者结合实际项目进行调试和验证,以获得更好的效果。
智能穿搭_ComfyUI_模型换装_SegVITON_实时服_1744169167.zip
3dmax插件
内容概要:本文详细介绍了基于飞思卡尔5634芯片的新能源电动车整车控制器(VCU)的开发过程和技术细节。主要内容涵盖硬件架构设计,如十路模拟量采集、高边和低边输出驱动及其保护机制;软件部分则涉及Matlab自动生成代码、扭矩控制逻辑以及CCP/INCA标定协议的应用。文中还分享了许多实战经验和常见问题解决方案,强调了量产级别的稳定性和安全性措施,如故障保护、标定接口的安全性以及高效的刷写方法。 适合人群:从事新能源汽车控制器开发的技术人员,尤其是有一定嵌入式系统开发经验的研发人员。 使用场景及目标:帮助开发者深入了解新能源电动车整车控制器的具体实现方式,掌握高效可靠的硬件设计和软件编程技巧,提高产品可靠性和性能。 其他说明:文章不仅提供了详细的代码片段和技术参数,还分享了大量来自实际项目的经验教训,有助于读者避开常见的开发陷阱并提升工作效率。
、在VB编辑器中添加自定义函数CallDeepSeekAPI,该函数通过HTTP请求调用阿里云的千问2.5大模型API,并处理API响应。第二种方法是通过office-A内容概要I助手插件:本文档详细,用户可从介绍了DeepSeek+W指定网站下载并PS的配置与安装该插件使用方法,由,通过微信登录尚硅谷讲师宋红康讲解。并设置内置大模型,如硅首先介绍了手动配置基流动的满的方法,包括安装血版R1VBA插件,同时提供了交互、配置WPS测试和其他高级功能宏安全性、设置VB模块(如。最后,还提供了如何在W添加阿里云千PS中添加受问2.5大模型的API信任加载项以及更多使用文档的函数)、添加模块链接。 适合人群作为AI助手以及:适合有一定编程创建模板以实现基础和技术背景的宏的自动启用WPS用户,。其次,文档还提供了另一种配置尤其是希望在WPS中集成AI方式——通过office助手进行智能写作-AI助手进行配置,具体步骤和文档处理的用户。 使用场景涵盖下载安装office及目标:-AI助手、在WPS内①适用于需要在WPS中集成设置内置大模型AI助手进行智能并进行交互测试。最后,文档写作、文档处理的用户;给出了wps中添加受信任加载②帮助用户通过手动配置或使用项的方法和Office插件快速集成AI助手的使用DeepSeek+WPS文档链接,确保,提升工作效率;用户能够顺利使用③提供详细的相关功能。 适合人群:对WPS有基本了解,希望提升办公配置步骤,确保用户能够顺利完成配置并正常使用AI助手效率或尝试AI的各项功能。 阅读建议:由于涉及到辅助写作的办公具体的配置步骤和技术人员及开发者。 细节,建议读者使用场景及目标在阅读过程中仔细:①希望通过WPS集成Deep按照步骤操作,并参考提供的链接获取Seek实现智能文本处理、问答等功能更多信息。对于初次配置的用户,;②利用建议先尝试简单的office-AI助手进行文章续写手动配置,熟悉后再考虑使用插、创作等高级件。办公任务;③解决WPS中第三方插件加载受限的问题,确保各类插件正常运行。 阅读建议:本文档内容详尽,实际操作性强,建议读者按照文中步骤逐一尝试,遇到问题可参考提供的补充说明或访问指定网站获取更多信息。同时,对于API密钥的获取和配置部分,务必仔细阅读官方指引,确保顺利完成配置。