BM算法

      在字符串匹配算法中,KMP算法并不是效率最高的,这里将介绍的是BM算法。BM算法是指模式串与源文本串的比较从右到左,其本质是对后缀蛮力匹配算法的改进。该算法在最坏情况下的时间复杂度为O(N)。


前提基础

      了解什么是坏字符,什么是好后缀。

  • 坏字符:当源文本串中的某个字符跟模式串的某个字符不匹配时,称源文本串中的这个失配字符为坏字符。
  • 好后缀:源文本串与模式串具有的最长的相同后缀串。

      图示说明如下:

           


BM算法

      BM算法定义了如下两个规则:

  • 坏字符规则:当出现坏字符时,模式串需要向右移动,后移位数 = 坏字符出现时模式串匹配到的位置 - 坏字符在模式串中上一次出现的位置,当”坏字符“不包含在模式串中,则上一次出现的位置为 -1。
  • 好字符规则:当匹配失败时,后移位数 = 好后缀在模式串中的位置 - 好后缀在模式串中上一次出现的位置,若好后缀在模式串中没有再次出现,则为 -1 。

      好字符规则的产生,是为了在某些情况下,能加大模式串的移动距离。举例如下:红色字符为已比较完成的字符,当前进行蓝色字符(即 S串中“I”字符与 P串中“A”)的比较,结果是失效。

            HERE IS A SIMPLE EXAMPLE     <== S串

                          EXAMPLE                     <== P串

      若此时按照坏字符规则,则 后移位数 = 2 - (-1)= 3位。

      若此时按照好后缀规则,在所有的“好后缀”(MPLE,PLE,LE,E)中,只有“E”在P串的头部出现了,因此 后移位数 = 6 - 0 = 6位。所以,这种情况下 应该采用规则2进行移动。

      综上,可以看出BM算法的基本思想就是,每次后移的位数是两个规则的较大值。


BM代码

public class BM {  
  
    /** 
     * 算法匹配 
     */  
    public static int pattern(String pattern, String target) {  
        int tLen = target.length();  
        int pLen = pattern.length();  
  
        if (pLen > tLen) {  
            return -1;  
        }  
  
        int[] bad_table = build_bad_table(pattern);  
        int[] good_table = build_good_table(pattern);  
  
        for (int i = pLen - 1, j; i < tLen;) {  
            System.out.println("跳跃位置:" + i);  
            for (j = pLen - 1; target.charAt(i) == pattern.charAt(j); i--, j--) {  
                if (j == 0) {  
                    System.out.println("匹配成功,位置:" + i);  
//                  i++;   // 多次匹配  
//                  break;  
                    return i;  
                }  
            }  
            i += Math.max(good_table[pLen - j - 1], bad_table[target.charAt(i)]);  
        }  
        return -1;  
    }  
  
    /** 
     * 字符信息表 
     */  
    public static int[] build_bad_table(String pattern) {  
        final int table_size = 256;  
        int[] bad_table = new int[table_size];  
        int pLen = pattern.length();  
  
        for (int i = 0; i < bad_table.length; i++) {  
            bad_table[i] = pLen;  //默认初始化全部为匹配字符串长度  
        }  
        for (int i = 0; i < pLen - 1; i++) {  
            int k = pattern.charAt(i);  
            bad_table[k] = pLen - 1 - i;  
        }  
        return bad_table;  
    }  
  
    /** 
     * 匹配偏移表。 
     * 
     * @param pattern 
     *            模式串 
     * @return 
     */  
    public static int[] build_good_table(String pattern) {  
        int pLen = pattern.length();  
        int[] good_table = new int[pLen];  
        int lastPrefixPosition = pLen;  
  
        for (int i = pLen - 1; i >= 0; --i) {  
            if (isPrefix(pattern, i + 1)) {  
                lastPrefixPosition = i + 1;  
            }  
            good_table[pLen - 1 - i] = lastPrefixPosition - i + pLen - 1;  
        }  
  
        for (int i = 0; i < pLen - 1; ++i) {  
            int slen = suffixLength(pattern, i);  
            good_table[slen] = pLen - 1 - i + slen;  
        }  
        return good_table;  
    }  
  
    /** 
     * 前缀匹配 
     */  
    private static boolean isPrefix(String pattern, int p) {  
        int patternLength = pattern.length();  
        for (int i = p, j = 0; i < patternLength; ++i, ++j) {  
            if (pattern.charAt(i) != pattern.charAt(j)) {  
                return false;  
            }  
        }  
        return true;  
    }  
  
    /** 
     * 后缀匹配 
     */  
    private static int suffixLength(String pattern, int p) {  
        int pLen = pattern.length();  
        int len = 0;  
        for (int i = p, j = pLen - 1; i >= 0 && pattern.charAt(i) == pattern.charAt(j); i--, j--) {  
            len += 1;  
        }  
        return len;  
    }  
}  


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值