BM 算法
介绍
1、该算法从模式串的尾部开始匹配,且拥有在最坏情况下O(N)的时间复杂度,是KMP算法的改进
2、坏字符规则
(1)当字符失配时,称文本串中的这个失配字符为坏字符,此时模式串需要向右移动,后移位数 = 坏字符匹配时在模式串中对应的索引 - 坏字符在模式串中最右出现的索引(匹配坏字符的字符)
(2)如果“坏字符”不包含在模式串之中,则最右出现位置为 -1
(3)有多个匹配的字符时,选择最靠近坏字符的字符,即选择最右方的前缀
(4)匹配坏字符的字符,可能出现的位置 = [0, 坏字符的索引]
3、好后缀规则
(1)当字符失配时,失配字符在模式串中对应位置的后方为好后缀,此时模式串需要向右移动,后移位数 = 好后缀在模式串中的位置 - 好后缀在模式串上一次出现的位置
(2)不论是好后缀,还是前缀,都以它最后一个字符的索引作为位置
(3)如果好后缀在模式串中没有再次出现(即没有匹配的前缀),则为 -1
(4)有多个匹配的前缀时,选择与“好后缀”匹配最多字符的前缀
(5)有多个相等字符的前缀时,选择最靠近好后缀的前缀,即选择最右方的前缀
(5)匹配好后缀的前缀,可能出现的位置 = [0, 好后缀最后一个字符的索引]
4、基本思想:每次后移这两个规则之中的较大值
5、时间复杂度:文本串长度为 n,模式串长度为 m
(1)最好 O(n / m)
(2)最坏 O(n * m)
代码实现
public class BM {
public static int bmMatch(String source, String target) {
int[] badChar = badChar(target);// 获得坏字符数值的数组,实现看下面
int[] goodSuffix = goodSuffix(target);// 获得好后缀数值的数组,实现看下面
for (int i = target.length() - 1, j; i < source.length(); ) {
System.out.println("跳跃位置:" + i);
for (j = target.length() - 1; source.charAt(i) == target.charAt(j); i--, j--) {
if (j == 0) {//指向模式串的首字符,说明匹配成功,直接返回就可以了
System.out.println("匹配成功,位置:" + i);
return i;
}
}
//如果出现坏字符,那么这个时候比较坏字符以及好后缀的数组,哪个大用哪个
i += Math.max(goodSuffix[target.length() - j - 1], badChar[source.charAt(i)]);
}
return -1;
}
//坏字符表
public static int[] badChar(String target) {
final int MAX_SIZE = 65536;
int[] badChar = new int[MAX_SIZE];//创建一个数组,用来记录坏字符出现时,应该跳过的字符数
for (int i = 0; i < badChar.length; i++) {
badChar[i] = target.length();//默认初始化全部为匹配字符串长度,假设主串中的坏字符在模式串中没有出现
}
for (int i = 0; i < target.length() - 1; i++) {
badChar[target.charAt(i)] = target.length() - 1 - i;
}
return badChar;
}
//匹配偏移表
public static int[] goodSuffix(String target) {
int[] goodSuffix = new int[target.length()];//创建一个数组,存好后缀数值
int lastPrefixPosition = target.length();//用于记录最新前缀的相对位置,初始化为模式串长度,当前后缀字符串为空
for (int i = target.length() - 1; i >= 0; i--) {
if (isPrefix(target, i + 1)) {
lastPrefixPosition = i + 1;//如果当前的位置存在前缀匹配,那么记录当前位置
}
goodSuffix[target.length() - 1 - i] = lastPrefixPosition - i + target.length() - 1;
}
for (int i = 0; i < target.length() - 1; ++i) {
int suffixLength = suffixLength(target, i);//计算出指定位置匹配的后缀的字符串长度
goodSuffix[suffixLength] = target.length() - 1 - i + suffixLength;
}
return goodSuffix;
}
//前缀匹配
private static boolean isPrefix(String target, int index) {
//这里j从模式串第一个字符开始,i从指定的字符位置开始,通过循环判断当前指定的位置p
//之后的字符串是否匹配模式串前缀
for (int i = index, j = 0; i < target.length(); ++i, ++j) {
if (target.charAt(i) != target.charAt(j)) {
return false;
}
}
return true;
}
//好后缀长度
private static int suffixLength(String target, int index) {
int suffixLength = 0;
for (int i = index, j =target.length() - 1; i >= 0 && target.charAt(i) == target.charAt(j); i--, j--) {
suffixLength += 1;
}
return suffixLength;
}
}
代码来自:不用找了,学习BM算法,这篇就够了(思路+详注代码)_BoCong-Deng的博客-CSDN博客_bm怎么计算
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战