字符串匹配算法(BFRK)

news2024/11/18 7:45:50

文章目录

    • 题目
    • 一、BF算法
    • 二、RK算法
    • 补充

题目

有字符串 str1 和 str2 ,str1 中是否包含 str2,如果没有包含返回 -1,如果包含,则返回 str2 在 str1 中开始的位置

注:保证 str1 和 str2 字符串的长度大于 0

举例:

在这里插入图片描述

可以看的出来,str2 字符串确实是 str1 的子串,并且 str2 在 str1 中第一次出现的位置是 2(字符串位置索引从 0 开始),因此返回 2

在这里插入图片描述

在上面的例子中,str1 中没有 str2 的子串,因此返回 -1

一、BF算法

BF算法,就是暴力算法,解决问题简单粗暴,缺点就是所消耗的代价较大,在一些极端情况下,效率极低

解题思路:

暴力解法的思路就是把 str1 字符串的每一个字符都尝试着作为 str2 的头向后进行匹配,如果匹配后发现是不同的字符,说明 str1 中无法以该字符为开始将 str2 匹配成功

例子演示:

1)从 str1 的下标 0 开始,进行字符的逐个比较,发现到了第三个字符就匹配失败了

在这里插入图片描述

2)将 str2 字符串向后移动一位,对应 str1 的下标 1 的字符,进行匹配,第一个字符就匹配失败了

在这里插入图片描述

3)将 str2 字符串向后移动一位,对应 str1 的下标 2 的字符,进行匹配,逐个比较,成功,返回 2

在这里插入图片描述

代码展示:

public static int BF(String str1,String str2) {
    int len1 = str1.length();
    int len2 = str2.length();
    //进入循环的条件是len1不小于len2,一共尝试匹配(len1-len2+1)轮
    for (int i = 0; i < (len1 - len2 + 1); i++) {
        int j = 0;
        for (; j < len2; j++) {
            //如果有个字符匹配不上了,说明从 str1 的位置i开始无法匹配出 str2
            if (str1.charAt(i + j) != str2.charAt(j)) {
                break;
            }
        }
        //若成立,表示从 str1 的位置 i 开始匹配出 str2,返回结果
        if (j == len2) {
            return i;
        }
    }
    return -1;
}

反思:

在某些极端的情况下,暴力算法的效率会很低

在这里插入图片描述

可以看出,上面的例子一共要比较 7 轮,并且每一轮都需要比较 3 次,因为除了最后一轮,其他的每轮比较都会发现前两个字符相同,最后一个字符不同

如果 str1 的长度为 N,str2 的长度为 M,那么最坏情况下,时间复杂度为 O(N*M)

二、RK算法

RK 算法实际上是建立在 BF 算法基础上的一种优化。

解题思路:

BF 算法之所以有效率低的缺点,就是因为在最坏的情况下需要将两个字符串的所有字符进行一次比对,如果我们将需要比对的两个字符串进行哈希,比对哈希值是否相等,就可以避免字符的挨个比较了。

如果哈希值不同,那么两个字符串一定不同,如果相同,再调用 equals 方法,精确比较

该算法的优化关键就在于进行了哈希,生成了 hashcode,那么哈希函数有很多种,这里采取的方法就是简单的按位相加,即将 ‘a’ 当成 1,‘b’ 当成 2,…,‘z’ 当成 26(假设字符串中只包含小写字母)

hash(“abcd”) = 1 + 2 + 3 + 4 = 10

这样的哈希生成算法简单,但是容易产生哈希碰撞,即明明不同的字符串,算出的哈希值却是相同的,比如 “abc” 和 “cba”,所以精确比较很有必要

例子演示:

1)可以算出 “abb” 的 hashcode 为 5,str1 中第一个长度为 3 的子串 “aba” 的 hashcode 为 4,不相等,说明匹配失败,进行下一轮
在这里插入图片描述

2)str2 向后移动一格,对应到的 str1 的字符串 "bab"的 hashcode 为 5,相等,进行精确比较,“bab”.equals(“abb”),返回 false,说明不匹配,进行下一轮

在这里插入图片描述

3)str2 向后移动一格,对应到的 str1 的字符串 "abb"的 hashcode 为 5,相等,进行精确比较,“abb”.equals(“abb”),返回 true,匹配成功,返回 2

在这里插入图片描述

代码展示:

public static int RK (String str1,String str2) {
    int len1 = str1.length();
    int len2 = str2.length();
    int str2code = hash(str2);//str2字符串的哈希值
    int str1code = hash(str1.substring(0,len2));//str1首次比对的子串的哈希值
    for (int i = 0; i < (len1-len2+1); i++) {
        if (str1code == str2code && realCompare(str1,str2,i)) {
            return i;
        }
        //说明还没有匹配成功,需要求出下一轮比对的str1子串的哈希值
        //如果已经是最后一轮了,就不需要更新,说明整体匹配失败
        if (i < len1-len2) {
            str1code = refreshHash(str1,str1code,i,len2);
        }
    }
    return -1;
}

//更新哈希值,为下一次的比对做准备
public static int refreshHash(String str1,int oldHash,int index,int len2) {
    oldHash -= (str1.charAt(index) - 'a' + 1);
    oldHash += (str1.charAt(index + len2) -'a' + 1);
    return oldHash;
}

//精确比对,index表示str1是从哪个位置开始比对的
public static boolean realCompare(String str1,String str2,int index) {
    return str2.equals(str1.substring(index,index + str2.length()));
}

//按位相加,哈希函数
public static int hash (String str) {
    int result = 0;
    for (int i = 0; i < str.length(); i++) {
        result += (str.charAt(i) - 'a' + 1);
    }
    return result;
}

反思:

计算哈希值的时候没有必要每次都将字符重新进行按位相加,可以利用之前计算出来的旧的哈希值来计算新的

例如 “abbc”,“abb” 的哈希值是 5(旧的哈希值),下一个要计算哈希值的字符串是 “bbc”,实际上就是在原来的字符串中减掉字符 ‘a’,加上字符 ‘c’,所以新的哈希值就是 7(5 - 1 + 3)

总结下来,如果 str1 的长度为 N,str2 的长度为 M,由于每个字符都会参与到哈希值的计算当中,RK 算法的时间复杂度为 O(N+M)

该算法的缺点也显而易见——性能不稳定。有产生哈希冲突的可能性,当冲突非常严重的时候,就需要经常进行精确比对,RK 算法和 BF 算法就差不多了

补充

问:那么有没有一种方法能够避免无谓的比对,又能够性能稳定,非常高效呢?

答:KMP 算法

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

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

相关文章

k8s-kubeadm安装1.25.5

准备环境&#xff1a; 想体验下新的版本 主机名IP资源k8s-master192.168.1.1912u2G内存20G磁盘k8s-node192.168.1.1922u2G内存20G磁盘 1 修改主机名&#xff0c;配置hosts文件 # 修改主机名 hostnamectl set-hostname k8s-master hostnamectl set-hostname k8s-node # 修改h…

CMMI2.0配置管理工作及访谈学习笔记(续)

1. 配置管理岗位职责 范围&#xff1a;组织级和项目级配置管理管理对象为过程和产品&#xff0c;产品为识别出的配置项建立配置库&#xff1a;为项目建立开发库&#xff08;管理库&#xff09;、基线库&#xff0c;建立配置库结构并分配权限&#xff08;命名规范&#xff09;基…

猿如意中的【DBeaver】工具详情介绍

猿如意中的【DBeaver】工具详情介绍一、工具名称二、下载安装渠道2.1 什么是猿如意&#xff1f;2.2 如何下载猿如意&#xff1f;2.3 如何在猿如意中下载开发工具&#xff1f;三、工具介绍四、DBeaver功能介绍五、软件截图六、DBeaver安装过程6.1 在猿如意中下载DBeaver6.2 选择…

道路裂缝坑洼图像开源数据集汇总

CrackForest数据集 数据集下载链接&#xff1a;http://suo.nz/2wdNdX CrackForest数据集是一个带注释的道路裂缝图像数据库&#xff0c;可以大致反映城市路面状况。 道路裂缝坑洼图像数据集 数据集下载链接&#xff1a;http://suo.nz/3eEDlj 这个数据集是一个极具挑战性的集…

67、INGeo:利用占用网格先验加速/减少迭代次数

简介 论文地址&#xff1a;INGeo: Accelerating Instant Neural Scene Reconstruction with Noisy Geometry Priors 首先我们知道Instant-ngp利用最先进的射线推进技术&#xff08;指数步进、空白跳过、样本压缩&#xff09;实现密集网格自剪枝的采样策略&#xff0c;这种采样…

代码随想录训练营第48天|LeetCode 198.打家劫舍、213.打家劫舍II、 337.打家劫舍III

参考 代码随想录 题目一&#xff1a;LeetCode 198.打家劫舍 确定dp数组下标及其含义 dp[i]&#xff1a;考虑下标i&#xff08;包括i&#xff09;以内的房屋&#xff0c;可以偷窃的最大金额为dp[i]。确定递推公式 如果当前的第i个房间不偷&#xff0c;那么dp[i] dp[i-1].如…

干货收藏 |关键词优化攻略!(附11款关键词检索工具)

关键字搜索是买家查找产品的方式&#xff0c;一个高质量的关键词可以帮助卖家快速增加曝光率。简而言之&#xff0c;利用好关键词机理&#xff0c;能让我们的产品产生更有利的排名因素。 那如何找到合适的关键字&#xff0c;设置关键字时应该注意什么&#xff1f; 今天从产品的…

libcrypto-1_1.dll丢失,要怎么处理?

一般出现这个libcrypto-1_1.dll丢失的问题&#xff0c;我看绝大部分都是出在刺客信条这边的人&#xff0c;很多人吐槽 在运行刺客信条3游戏的时候遇到提示缺少libcef.dll文件的问题。其实遇到这问题还是比较好解决的。 libcrypto-1_1.dll丢失的处理方法 第一种&#xff0c;首…

云存储--1

背景 这一板块主要是讲诉云计算中的存储板块。 那么云存储主要分为三大类&#xff1a;块存储、文件存储、对象存储。 那么&#xff0c;这一章我们来了解一下什么是块存储&#xff0c;以及块存储在云计算当中的使用场景。 1、 什么是块存储&#xff1f; 我们来思考一个场景&a…

Spring Boot集成MyBatis

1.整合Durid数据源 1、引入Jar包 2.application.yml配置 3.读取配置类DruidConfig 总结&#xff1a;其实没有必要一个个手动去配置&#xff0c; druid 启动starter druid 自动配置类 2.整合MyBatis 2.1生成MyBatis代码: pom.xml generatorConfig.xml 运行插件—…

java计算机毕业设计ssm余庆金阳驾校管理系统75wh9(附源码、数据库)

java计算机毕业设计ssm余庆金阳驾校管理系统75wh9&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#…

看完这篇,轻松编写Markdown

攻城狮为什么要会Markdown Markdown是什么 Markdown是一种轻量级标记语言&#xff0c;创始人为约翰格鲁伯&#xff08;John Gruber&#xff09;。它允许人们使用易读易写的纯文本格式编写文档&#xff0c;然后转换成有效的 XHTML&#xff08;或者HTML&#xff09;文档。这种语…

【网络攻防】Linux提权(待更)

Linux提权 文章目录Linux提权1.利用suid提权&#xff08;1&#xff09;如何设置suid&#xff08;2&#xff09;如何找到设置了suid的程序&#xff08;3&#xff09; suid提权2.利用环境变量提权&#xff08;1&#xff09;创建拥有 suid 权限的程序&#xff08;2&#xff09;劫持…

vue innerHTML 绑定单击事件不生效

在使用 vue时候对 innerHTML进行绑定单击事件&#xff0c;绑定后事件不生效 原代码 div.innerHTML "<el-button sizemini typetext clickhandleUpdate1("JSON.stringify(warnCntItem)") stylecolor: #f56c6c> "warnCntItem.warnCnt"</el-b…

【云计算与大数据技术】分布式协同系统Chubby锁、ZooKeeper在HDFS中的使用讲解(图文解释 超详细)

阿里云Kuafa RPC系统 夸父(Kuafu)是飞天平台内核中负责网络通信的模块&#xff0c;它提供了一 个 RPC 的接口 , 简化编写基于网络的分布式应用 夸父的设计目标是提供高可用(724小时)、大吞吐量(Gigabyte)、高效率、易用(简明 API、多种协议和编程接口)的 RPC服务 Hadoop IPC…

【软件测试】资深测试的建议。初入测试行的小伙伴,准备起航吧......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 大部分初级的岗位&a…

Java面试题总结-抽象类和接口的区别

抽象类是用来捕捉子类的通用特性的。接口是抽象方法的集合。 从设计层面来说&#xff0c;抽象类是对类的抽象&#xff0c;是一种模板设计&#xff0c;接口是行为的抽象&#xff0c;是一种行为的规范。 想要了解抽象类和接口的区别&#xff0c;我们首先要想清楚抽象类和接口的相…

Word控件Spire.Doc 【超链接】教程(3):在C#中查找word文档中的超链接

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

服务端监控工具:Nmon使用方法

目录&#xff1a;导读 一、认识nmon 二、检查安装环境 三、nmon下载安装 四、运行nmon 五、采集数据 总结 写在最后 一、认识nmon 1、简介 nmon是一种在AIX与各种Linux操作系统上广泛使用的监控与分析工具&#xff0c;它能在系统运行过程中实时地捕捉系统资源的使用情…

再学C语言1:开发环境

重新把C语言梳理一遍&#xff0c;学习在VSCode中进行C语言编程。 因此第一步是把环境配置起来。 步骤一&#xff1a;下载、安装VSCode 官网下载即可&#xff0c;地址&#xff1a;https://code.visualstudio.com/Visual Studio Code is a code editor redefined and optimize…