字符串匹配算法之——BM算法

字符串匹配的暴力算法就不细说了,主要思想就是从头比较模式串和主串,如果当前的字符匹配就比较下一位;如果不匹配则模式串向后移动一位和主串的下一位开始比较。

我们下面来讲BM的算法流程,最后会贴出代码。

德克萨斯大学的 Robert S. Boyer 教授和 J Strother Moore 教授发明了这种算法 称为BM算法。

BM算法有个不同点:让"主串" 与 “模式串” 头部对齐,从尾部开始比较。

BM算法有2个规则坏字符好后缀

假定主串为 “HERE IS A SIMPLE EXAMPLE”,模式串为 “EXAMPLE”。

一、坏字符原则:

如果主串和模式串的相应的字符不匹配就叫坏字符,那么主串移动的位数等于: 坏字符在模式串中的位置 减去 坏字符在模式串中最右出现的位置如果坏字符在模式串中找不到那么就 减去 -1

字符串匹配算法之——BM算法_第1张图片
首先,模式串的 E 和 主串的 S不相等,那么模式串移动的位数为: E在模式中中的位置为 6, 而S在模式串找不到,那么 移动位数等于 6 - (-1)= 7.

移动7位后的结果如下:
字符串匹配算法之——BM算法_第2张图片
再比较模式串的 E 和 主串的 P,也不匹配,但是 P在模式串中最右的位数是4,所以模式串移动的位数为 6-4 = 2;

移动2位的结果如下:
字符串匹配算法之——BM算法_第3张图片
然后依次比较发现MPLE都是相同的,到模式串的A 和 主串的 I 不匹配,即 I 为坏字符,I 在模式串中对应的位置为 2,而 I 不在模式串中 ,所以模式串移动的次数等于: 2- (-1)= 3。这里要注意了,按坏字符原则 模式串要移动3位,但是 按照好后缀的原则,模式串应该移动几位呢。

MPLE、PLE、LE、E都是好后缀,好后缀 移动的位数等于:好后缀的位置 - 搜索词中的上一次出现位置

好后缀 MPLE 在 模式串的前面字符 EXM 中没有,PLE,在 EXAM 中也没出现,LE 在 EXAMP 中 也没出现,E 在模式串 的第 0 为出现了,所以,
移动的位数,好后缀 E 对应于 模式串的 第6 位,而 他上一次出现的位置是0,所以 移动的位数是 6 - 0 = 6; 而刚刚说的 坏字符 原则只能移动3位 。所以采用 好后缀 原则。

BM 算法就是 遇到 不匹配的字符,每次都会计算 坏字符好后缀移动的步数取最大值。移动 6 位后的结果如下:
字符串匹配算法之——BM算法_第4张图片
然后 比较 主串的 P 和模式串的E
字符串匹配算法之——BM算法_第5张图片
发现是 坏字符,根据 坏字符 移动的位数等于:P 对应模式串的位置是6, P在模式串中最右出现的位置是 4,移动 6 - 4 = 2 位;移动后的结果如下。

字符串匹配算法之——BM算法_第6张图片
然后从最后一位开始比较,发现最后完全匹配上了,流程结束。

根据上面的原则,相应的代码如下:

public class BM {
    /**
     * 利用坏字符规则计算移动位数:坏字符 对应在模式串中的位置 - 从右往左搜索坏字符在模式串中的位置,
     * 如果 坏字符在模式串中未找到,那么 就 减去 (-1)
     */
    public static int badCharacter(String moduleString, char badChar, int badCharSuffix) {
        return badCharSuffix - moduleString.lastIndexOf(badChar, badCharSuffix);//lastIndexOf 找不到就返回 -1, badCharSuffix 表示从badCharSuffix这个位置开始,往左边查找
    }

    /**
     * 利用好后缀规则计算移动位数
     */
    public static int goodPostSuffix(String moduleString, int goodCharSuffix) {
        int result = -1;
        // 模式串长度
        int moduleLength = moduleString.length();
        // 好字符数
        int goodCharNum = moduleLength - 1 - goodCharSuffix;

        for (; goodCharNum > 0; goodCharNum--) {
            String endSection = moduleString.substring(moduleLength - goodCharNum, moduleLength);
            String startSection = moduleString.substring(0, goodCharNum);
            if (startSection.equals(endSection)) {
                result = moduleLength - goodCharNum;
            }
        }
        return result;
    }

    /**
     * BM匹配字符串
     *
     * @param originString 主串
     * @param moduleString 模式串
     * @return 若匹配成功,返回下标,否则返回-1
     */
    public static int match(String originString, String moduleString) {
        // 主串
        if (originString == null || originString.length() <= 0) {
            return -1;
        }
        // 模式串
        if (moduleString == null || moduleString.length() <= 0) {
            return -1;
        }
        // 如果模式串的长度大于主串的长度,那么一定不匹配
        if (originString.length() < moduleString.length()) {
            return -1;
        }

        int moduleSuffix = moduleString.length() - 1;
        int moduleIndex = moduleSuffix;
        int originIndex = moduleSuffix;
        int originLength = originString.length();

        for (int i = originIndex; originIndex < originLength && moduleIndex >= 0;) {
            char oc = originString.charAt(originIndex);
            char mc = moduleString.charAt(moduleIndex);
            if (oc == mc) {
                originIndex--;
                moduleIndex--;
            } else {
                // 坏字符规则
                int badMove = badCharacter(moduleString, oc, moduleIndex);
                // 好字符规则
                int goodMove = goodPostSuffix(moduleString, moduleIndex);
                // 下面两句代码可以这样理解,主串位置不动,模式串向右移动
                originIndex = i + Math.max(badMove, goodMove);
                moduleIndex = moduleSuffix;
                // ot就是中间变量
                i = originIndex;
            }
        }
        if (moduleIndex < 0) {
            // 多减了一次
            return originIndex + 1;
        }
        return -1;
    }

    public static void main(String[] args) {
        // 主串
        String originString = "HERE IS A SIMPLE EXAMPLE";
        // 模式串
        String moduleString = "EXAMPLE";
        int index = match(originString, moduleString);
        System.out.println("匹配的下标:" + index);
    }
}

你可能感兴趣的:(Java,语言)