字符串匹配算法(二)BM算法

news2024/11/24 12:33:38

文章目录

  • 算法简介
    • 坏字符规则
      • 坏字符的定义
      • 坏字符的移动
    • 好后缀规则
      • 好后缀的定义
      • 好后缀的移动
  • 算法实现

算法简介

  • BM算法也就是Boyer Moore算法,它是一种非常高效的字符串匹配算法,是一种滑动算法。
  • 什么是滑动?
    下面例子中,主串中的c,在模式串中不存在,所以模式串可以往后华滑动,因为只要有c存在,那么该字串肯定与模式串不匹配。
    在这里插入图片描述
  • BM算法,本质上其实就是在寻找这种规律,借助这种规律可以高效的实现字符串匹配。为了实现正确的滑动,BM算法包含了两个部分,分别是坏字符规则(bad character rule)和好后缀规则(good suffix shift)

坏字符规则

坏字符的定义

BM算法的匹配顺序是按照模式串的下标从大到小的顺序进行匹配的,当模式串的末尾倒着匹配中,发现某个字符没法匹配,我们就叫主串中的这个字符是坏字符
在这里插入图片描述

坏字符的移动

当模式串和主串的某个字串发生不匹配时,我们把坏字符串对应模式串的那个字符下标记作si,如果坏字符串在模式串中存在,我们把这个坏字符串在模式串中的下标记作xi,如果不在模式串中存在,则xi = -1。那么此时,模式串往后移动的位数就等于si - xi 。
在这里插入图片描述

好后缀规则

好后缀的定义

在模式串和主串字串匹配过程中,匹配上的字符串我们叫做好后缀
在这里插入图片描述

好后缀的移动

  • 我们把已经匹配的子串,在模式串中查找,如果找到另外一个跟好后缀匹配的模式串子串{u},此时,移动位数=好后缀在模式串中的当前位置 - 好后缀在模式串上一次出现的位置(模式串倒数第二个好后缀),如果好后缀只在模式串中出现一次,则上一次出现位置的值为-1.
    在这里插入图片描述
  • 如果在模式串中找不到另外一个等于好后缀的字串,那么我们就直接将模式串,滑动到主串好后缀的后面。
    在这里插入图片描述
  • 过度滑动的情况:当模式串滑动到前缀与主串中{u}的后缀有部分重合的时候,并且重合的部分相等的时候,就有可能会存在完全匹配的情况。针对这种情况,我们不仅要看好后缀在模式串中,是否有另一个匹配的子串,我们还要考察好后缀的后缀子串(c),是否存在跟模式串的前缀子串(c)匹配的。
    在这里插入图片描述

算法实现

package com.xxliao.algorithms.string_match.bm;

import java.util.Arrays;

/**
 * @author xxliao
 * @description: 字符串匹配 BM算法
 * @date 2024/5/31 16:19
 */

public class BMMatch {
    
    public static void main(String[] args) {
        String main = "acabcbcbacabc";
        String pattern = "cbacabc";
        System.out.println(indexOf(main,pattern));
    }

    private static final int CHARACTER_SIZE = 256; // 英文字符的种类,2^8

    /**
     * @description  BM算法匹配字符串,匹配成功返回P在S中的首字符下标,匹配失败返回-1
     * @author  xxliao
     * @date  2024/5/31 16:20
     */
    public static int indexOf(String main, String pattern) {

        char[] main_array = main.toCharArray();
        char[] pattern_array = pattern.toCharArray();
        int main_length = main_array.length;
        int pattern_length = pattern_array.length;

        // 模式串为空字符串,返回0
        if (pattern_length == 0) {
            return 0;
        }
        // 主串长度小于模式串长度,返回-1
        if (main_length < pattern_length) {
            return -1;
        }

        int[] bad_char_array = buildBadCharacter(pattern_array);
        int[] good_char_array = buildGoodSuffix(pattern_array);

        // 从尾部开始匹配,其中i指向主串,j指向模式串
        for (int i = pattern_length - 1; i < main_length; ) {
            int j = pattern_length - 1;
            for (; main_array[i] == pattern_array[j]; i--, j--) {
                if (j == 0) {   // 匹配成功返回首字符下标
                    return i;
                }
            }
            // 每次后移“坏字符规则”和“好后缀规则”两者的较大值
            // 注意此时i(坏字符)已经向前移动,所以并非真正意义上的规则
            i += Math.max(bad_char_array[main_array[i]], good_char_array[pattern_length - 1 - j]);
        }

        return -1;
    }

    /**
     * @description  坏字符规则表,数组内默认填充的是后移位数
     * @author  xxliao
     * @date  2024/5/31 16:30
     */
    private static int[] buildBadCharacter(char[] pattern) {
        int pattern_length = pattern.length;
        int[] bad_char_array = new int[CHARACTER_SIZE]; // 记录坏字符出现时后移位数

        Arrays.fill(bad_char_array, pattern_length);  // 默认后移整个模式串长度

        for (int i = 0; i < pattern_length - 1; i++) {
            int ascii = pattern[i];  // 当前字符对应的ASCII值
            bad_char_array[ascii] = pattern_length - 1 - i;   // 对应的后移位数,若重复则以最右边为准
        }

        return bad_char_array;
    }

    /**
     * @description  非真正意义上的好字符规则表,后移位数还加上了当前好后缀的最大长度
     * @author  xxliao
     * @date  2024/5/31 16:30
     */
    private static int[] buildGoodSuffix(char[] pattern) {
        int pattern_length = pattern.length;
        int[] good_char_array = new int[pattern_length];   // 记录好后缀出现时后移位数
        int last_prefix_pos = pattern_length;   // 好后缀的首字符位置

        for (int i = pattern_length - 1; i >= 0; i--) {
            // 判断当前位置(不含)之后是否是好后缀,空字符也是好后缀
            if (isPrefix(pattern, i + 1)) {
                last_prefix_pos = i + 1;
            }
            // 如果是好后缀,则GS=pLen,否则依次为pLen+1、pLen+2、...
            good_char_array[pattern_length - 1 - i] = last_prefix_pos - i + pattern_length - 1;
        }

        // 上面在比较好后缀时,是从模式串的首字符开始的,但实际上好后缀可能出现在模式串中间。
        // 比如模式串EXAMPXA,假设主串指针在比较P时发现是坏字符,那么XA就是好后缀,
        // 虽然它的首字符X与模式串的首字符E并不相等。此时suffixLen=2表示将主串指针后移至模式串末尾,
        // pLen-1-i=4表示真正的好字符规则,同样主串指针后移,使得模式串前面的XA对齐主串的XA
        for (int i = 0; i < pattern_length - 1; i++) {
            int suffixLen = suffixLength(pattern, i);
            good_char_array[suffixLen] = pattern_length - 1 - i + suffixLen;
        }

        return good_char_array;
    }

    /**
     * @description  判断是否是好后缀,即模式串begin(含)之后的子串是否匹配模式串的前缀
     * @author  xxliao
     * @date  2024/5/31 16:30
     */
    private static boolean isPrefix(char[] pattern, int begin) {
        for (int i = begin, j = 0; i < pattern.length; i++, j++) {
            if (pattern[i] != pattern[j]) {
                return false;
            }
        }

        return true;
    }

    /**
     * @description  返回模式串中以pattern[begin](含)结尾的后缀子串的最大长度
     * @author  xxliao
     * @date  2024/5/31 16:30
     */
    private static int suffixLength(char[] pattern, int begin) {
        int suffixLen = 0;

        int i = begin;
        int j = pattern.length - 1;
        while (i >= 0 && pattern[i] == pattern[j]) {
            suffixLen++;
            i--;
            j--;
        }
        return suffixLen;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1718392.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vcruntime140_1.dll在哪个文件夹?详细修复vcruntime140_1.dll缺失的方法

vcruntime140_1.dll文件是什么&#xff1f;相信很多人都对它很陌生吧&#xff1f;毕竟大部分人对于dll文件还是了解得太少了&#xff0c;当突发情况出现vcruntime140_1.dll文件丢失&#xff1f;你要怎么办&#xff1f;不要担心&#xff0c;下面我们就来给大家详细的讲解一下修复…

出吉林大学计算机考研资料适用于计专966/计学941/软专967

本人是24上岸吉大计算机专硕的考生&#xff0c;先上成绩&#xff1a; 出专业课备考过程的相关笔记资料&#xff0c;也可以提供经验分享等&#xff1a; 吉林大学计算机数据结构基础算法ADL汇总&#xff0c;适用于计专966/计学941/软专967综合整理小绿书以及期末题上重难点算法…

LLVM入门教学——Code Coverage插桩

1、简介 LLVM的Code Coverage工具集提供了一系列工具和库&#xff0c;帮助开发者收集和分析代码覆盖率数据&#xff0c;从而评估测试的有效性和代码的执行情况。LLVM的Code Coverage工具集包括以下主要组件&#xff1a; Clang编译器&#xff1a;用于编译源代码并生成覆盖率信息…

Linux下配置Pytorch

1.Anaconda 1.1虚拟环境创建 2.Nvidia驱动 3.CUDA驱动安装 4.Pytorch安装 具体的步骤如上&#xff1a;可参考另一位博主的博客非常详细&#xff1a; Linux服务器配置PythonPyTorchCUDA深度学习环境_linux cuda环境配置-CSDN博客https://blog.csdn.net/NSJim/article/detai…

官方小游戏项目

一 项目原理&#xff1a;看广告&#xff0c;操作简单&#xff0c;时间自由&#xff0c;适合利用业余时间来做&#xff0c;一个广告大概在15s-30s之间。 二 介绍&#xff1a;给你开代理权限&#xff0c;你就有独立后台管理系统&#xff0c;监测每台手机每条广告的情况&#xff0…

探索Web3工具:正确使用区块链平台工具的秘诀

在当今日新月异的数字时代&#xff0c;区块链技术正以惊人的速度改变着我们的生活和工作方式。尤其对于那些想要踏入区块链世界的人来说&#xff0c;正确使用区块链平台工具至关重要。本文将向您介绍一些关键的Web3工具&#xff0c;并以TestnetX.com为例&#xff0c;展示如何利…

第七在线惊艳亮相第11届奥莱峰会,AI驱动零售供应链升级

2024年5月22-24日&#xff0c;第11届奥莱领秀峰会暨2024奥莱产业经济论坛在南京盛大举行。论坛上&#xff0c;智能商品计划管理系统服务商第七在线凭借富有前瞻性的AI技术&#xff0c;引领零售供应链迈入全新升级阶段&#xff0c;赢得了与会嘉宾的广泛关注与赞誉。 峰会由中国奥…

《Kubernetes部署篇:基于麒麟V10+ARM64架构部署harbor v2.4.0镜像仓库》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;企业级K8s集群运维实战 一、环境信息 K8S版本 操作系统 CPU架构 服务版本 1.26.15 Kylin Linux Advanced Server V10 ARM64 harbor v2.4.0 二、部…

使用element的小弹框并修改css

使用el-popover来做弹框&#xff1a; 滑动或点击元素要加插槽slot"reference"来展示弹框&#xff1b; <el-popoverplacement"top"width"166"trigger"hover"popper-class"popover"><div><div><div>…

安装 Android Studio 2024.1.1.6(Koala SDK35)和过程问题解决

记录更新Android Studio版本及适配Android V应用配置的一些过程问题。 安装包&#xff1a;android-studio-2024.1.1.6-windows.exe原版本&#xff1a;Android Studio23.2.1.23 Koala 安装过程 Uninstall old version 不会删除原本配置&#xff08;左下角提示&#xff09; Un…

配置华为路由器通过RADIUS对接安当ASP身份认证服务器以实现上网功能解决方案

当配置华为路由器通过RADIUS对接安当ASP身份认证服务器以实现上网功能时&#xff0c;以下是一个更详细的解决方案&#xff1a; 一、前期准备 1. 确认网络环境&#xff1a; 确保华为路由器与安当ASP身份认证服务器之间的网络连接稳定可靠。确定RADIUS协议所需的端口&#xff08…

博客星球大冒险:用Spring Boot和JWT打造你的数字王国

揭秘如何在Spring Boot中无缝集成JWT&#xff0c;为你的应用打造一个高度可扩展且安全的认证系统。从添加依赖到创建JWT过滤器&#xff0c;再到实现令牌的有效性管理和刷新机制&#xff0c;每一步都精心设计&#xff0c;确保你的乐园能够迎接成千上万的游客&#xff01; 文章目…

HOW - BFF 服务实践系列(一)

目录 一、BFF 介绍1.1 BFF 的概念1.2 为什么需要 BFF1.3 举例说明 二、适用于Web前端的BFF应该提供哪些能力2.1 接口聚合&#xff08;重要&#xff09;2.2 简化和优化的API2.3 安全和身份验证&#xff08;重要&#xff09;2.4 缓存机制2.5 错误处理和重试机制2.6 数据格式转换2…

Nvidia Orin/Jetson +GMSL/RLINC/VbyOne/FPDLink 同轴AI多相机同步车载视觉解决方案

在本次演讲中&#xff0c;介绍了多相机同步技术在自主机器中的应用情况&#xff0c;围绕无人配送小车、控制器视觉传感器方案升级、人形机器人三个典型案例中如何为客户提供高效的多相机同步解决方案进行了详细的讲解&#xff0c;并进一步介绍如何通过创新的多相机同步技术&…

掌控未来,爱普生SR3225SAA用于汽车钥匙、射频电路的智慧引擎

为了响应市场需求&#xff0c;Epson使用独家QMEMS*2技术所生产的石英振荡器&#xff0c;与其精巧的半导体技术所制造的射频传输器电路&#xff0c;开发了SR3225SAA。不仅内建的石英震荡器之频率误差仅有2 ppm&#xff0c;更使其封装尺寸达仅3.2 mm x 2.5 mm&#xff0c;为客户大…

Maven简介和快速入门

1.1Maven介绍 Maven – Introduction (apache.org) Maven就是一个软件&#xff0c;掌握软件安装、配置、以及基本功能&#xff08;项目构建、依赖管理&#xff09;。 1.2Maven主要作用 1.依赖管理&#xff1a; Maven 可以管理项目的依赖&#xff0c;包括自动下载所需依赖库、…

Aigtek功率放大器的主要性能要求有哪些

功率放大器是电子系统中的重要组件&#xff0c;用于将低功率信号放大到高功率水平。功率放大器的性能直接影响到信号的放大质量和系统的整体性能。下面西安安泰将介绍功率放大器的主要性能要求。 增益&#xff1a;功率放大器应当具有足够的增益&#xff0c;即将输入信号的幅度放…

机器人控制系列教程之D-H参数建模法

机器人运动学的研究依赖于机器人的模型的建立&#xff0c;目前较为多见的两种方法分别是Denavit-Hartenberg建模法&#xff08;简称&#xff1a;D-H建模法&#xff09;。该方法时由Denavit和Hartenberg于19955年提出的一种为关节链中的每一个杆件建立一个坐标系的矩阵方法&…

VIO System 丨适用于控制器开发前期的测试系统

VIO综述 嵌入式软件的HIL测试需要复杂的测试系统及完整的ECU硬件&#xff0c;这导致通常只能在开发流程的后期阶段进行测试。全新推出的低成本解决方案VIO System&#xff0c;使得在开发前期不仅可以进行总线通讯测试&#xff0c;也可以同时进行I/O信号测试。 该系统旨在通过…

产品上市新闻稿怎么写?纯干货

一个产品的上市&#xff0c;想要达到一个非常好的宣传效果&#xff0c;前期的预热造势是必不可少的&#xff0c;投放产品上市新闻稿到权威专业的媒体&#xff0c;潜移默化去影响用户的心智&#xff0c;产品上市新闻稿怎么写&#xff1f;接下来伯乐网络传媒就来给大家分享一下&a…