BM算法(Boyer-Moore) (JAVA实现)

时间:2023-02-10 14:02:33

BM算法时间上也是O(M+N),而且可以跳着search,但不适合characterset太小的状况;

BM算法主要涉及两个主要步骤:BadCharacter rule 和 Good Suffix rule,并且右后往前比对。

1.建立字符表,坏字符规则BadCharacter rule):

思想:

字符表中的每个字符在匹配的的规则字符串(pattern)是否出现过,若没有出现,则直接整体跳过,因为在当前字符出现的匹配范围内不可能匹配到;

若字符表中的字符在匹配串中出现,则记下出现的位置。以此建立一个字符table[]如下图:

A 2,5
B 3,6
C 7
D 7
E 7
.
.
.
 
X 7
Y 7
Z 7
   


2.建立匹配表,好后缀规则 Good Suffix rule

此步骤主要试分析匹配串,分析首位出现重复的最大长度以及串中子串的后缀是否在前面出现过并记录多少步能走到前面重复出现的位置。

BM算法(Boyer-Moore) (JAVA实现)BM算法(Boyer-Moore) (JAVA实现)

T P A B X A B
7 7 7 7 3 3 0

如上图便是好后缀规则,首先,上述字符串首位没有重复部分,因此重复长度k=0,pattern总长度为m=7;接着逐个分析(从右到左):B无后缀,因此为0,A后缀为B,但A前面走3步便得到重复后缀B,因此A为3,X后缀为AB,但前面走3步就得到重复后缀AB,因此X对应为3,B后缀为XAB,但XAB前面没有重复出现,因此对应为m-k=7-0=7,以此类推,建立如上好后缀table。


3.跳跃匹配,从右到左匹配(代码部分参考资料)

package com.wxshi.algorithms;

/**
* 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);// 1,3,5,6,2,
int[] good_table = build_good_table(pattern);// 1,8,5,10,11,12,13

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;
}
// for (int i : bad_table) {
// if (i != 7) {
// System.out.print(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;
}

}