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怎么计算

posted @   半条咸鱼  阅读(448)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示