参考的该篇文章:字符串匹配算法(BM)
https://blog.csdn.net/qq_21201267/article/details/92799488
详细推倒过程参考上面的链接,以下仅是个人笔记
1求坏字符
//坏字符规则:获得坏字符数组
//该int[]浪费空间,可以优化,badChars[c-‘a’] = i;
public static void getBc(String pat, int[] badChars) {
int len = pat.length();
for (int i = 0; i < size; i++) {
badChars[i]=-1;
}
for (int i = 0; i < len; i++) {
char c = pat.charAt(i);
badChars[c] = i;
}
}
2仅使用坏字符规则实现的BM
//仅使用坏字符规则实现的BM
public static int StrBm(String tar, String pat) {
int len1 = tar.length();
int len2 = pat.length();
int[] badChars = new int[size];
getBc(pat, badChars);
int i = 0;
int j;
while (i <= len1 - len2) {
for (j = len2 - 1; j >= 0; j--) {
if (tar.charAt(i + j) != pat.charAt(j)) {
break;
}
}
//匹配成功,直接返回
if (j < 0) {
return i;
}
i = i + j - badChars[tar.charAt(i + j)];
}
return -1;
}
3使用HashMap存储坏字符
//使用HashMap存储每个坏字符在模式串中的位置,节省坏字符数组大小
public static void getBc1(String pat, Map<Character,Integer> badChars) {
int len = pat.length();
for (int i = 0; i < len; i++) {
badChars.put(pat.charAt(i), i);
}
}
//仅使用坏字符规则实现的BM
public static int StrBm1(String tar, String pat) {
int len1 = tar.length();
int len2 = pat.length();
Map<Character,Integer> badChars = new HashMap<>();
getBc1(pat, badChars);
int i = 0;
int j;
while (i <= len1 - len2) {
for (j = len2 - 1; j >= 0; j--) {
if (tar.charAt(i + j) != pat.charAt(j)) {
break;
}
}
if (j < 0) {
return i;
}
int move = j - (null == badChars.get(tar.charAt(i + j)) ? -1 : badChars.get(tar.charAt(i + j)));
i = i + move;
}
return -1;
}
4求好后缀
//好后缀规则
public static void getGs(String pat, int[] suffix, boolean[] prefix) {
int len = pat.length();
for (int i = 0; i < len; i++) {
suffix[i] = -1;
prefix[i] = false;
}
int i;//依次从前往后遍历模式串
int j;//
int k;
for (i=0;i<=len-2;i++){
//每次j和i为模式串前缀的终点,从后开始往前与后缀匹配,所以j--;
j=i;
//记录前缀子串和后缀子串共有的长度
k=0;
while (j>=0 &&pat.charAt(j)==pat.charAt(len-1-k)){
j--;
k++;
suffix[k]=j+1;
}
//查找到模式串的头部了
if(j<0){
prefix[k]=true;
}
}
}
5好后缀移动位数(三种情况)
public static int getBsMove(int j, int len, int[] suffix, boolean[] prefix){
//好后缀长度
int k = len - 1 - j;
//第一种情况,好后缀数组里面存在匹配的字符串
if (suffix[k] != -1) {
return j+1 -suffix[k];
}
//第二种情况,好后缀的后缀子串和模式串的前缀子串有相等的
for (int r = j + 2; r < len; r++) {
if(prefix[len-r]==true){
return r;
}
}
//第三种情况,都没有匹配的,直接移动模式串长度
return len;
}
6使用坏字符规则和好后缀规则实现的BM
//使用坏字符规则和好后缀规则实现的BM
public static int StrBm2(String tar, String pat) {
int len1 = tar.length();
int len2 = pat.length();
//获得坏字符数组
Map<Character, Integer> badChars = new HashMap<>();
getBc1(pat, badChars);
//获得好后缀数组
int[] suffix = new int[len2];
boolean[] prefix = new boolean[len2];
getGs(pat, suffix, prefix);
int move1 = 0;//使用坏字符规则移动的个数
int move2 = 0;//使用好后缀规则移动的个数
int i = 0;
int j;
while (i <= len1 - len2) {
for (j = len2 - 1; j >= 0; j--) {
if (tar.charAt(i + j) != pat.charAt(j)) {
break;
}
}
//匹配成功
if (j < 0) {
return i;
}
move1 = j - (null == badChars.get(tar.charAt(i + j)) ? -1 : badChars.get(tar.charAt(i + j)));
move2 = 0;//重要,这个不能忘,否则下一步出错
//说明存在好后缀
if (j < len2 - 1) {
move2 = getBsMove(j, len2, suffix, prefix);
}
i = i + Math.max(move1, move2);
}
return -1;
}