【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化
字符串匹配算法是在实际工程中经常遇到的问题,也是各大公司笔试面试的常考题目,本文主要介绍BF算法(最好想到的算法,也最好实现)和KMP算法(最经典的)
一、BF算法
BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和T的第二个字符,若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力法。 ---这段话来自百度百科
这段话晦涩难懂,需要例子支持。
下面我们就通过例子来解释这个问题。 l假定我们给出字符串“ababcabccabcacbab”作为主串,然后给出子串:“abcac”现在我们需要查找子串是否在主串中出现,出现返回主串中的第一个匹配的下标,失败返回-1;
1.图解:
2.代码实现:
思路:
分别用 i 和 j 来遍历 主串 和 子串 ;
当主串和子串字符相同 i++ ,j++ ;
不同时 i = i - j +1 (i从下一个i开始继续遍历) j = 0(子串回到开头);
直到 j >= lenSub (子串遍历完了) 返回 i - j (主串中开始匹配的其实位置)
在Java中str == null和str.length == 0的区别:
str == null表示 str 没有指向任何对象,就是没有对应堆中对象
str.length() == 0表示 str 指向一个字符串对象,但是这个字符串长度为0
//str代表主串 sub代表子串public static int BF(String str, String sub) {if (str == null || sub == null) {return -1;}int lenStr = str.length();int lenSub = sub.length();if (lenStr == 0 || lenSub == 0) {return -1;}int i = 0;//遍历主串int j = 0;//遍历子串while (i < lenStr && j < lenSub) {if (str.charAt(i) == sub.charAt(j)) {i++;j++;} else {i = i - j + 1;j = 0;}}//子串遍历完了if (j >= lenSub) {return i - j;}return -1;}
二、KMP算法
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特莫里斯一普拉特操作(简称KMP算法) 。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next( )函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)
---这段话来自百度百科
1. KMP算法解决的问题
对某些情况下的BF算法进行优化
BF算法每次字符串匹配失败,子串的 j 都会回到子串的第一个字符,但是我们看下面这个图会发现在有些情况下这样的回退是没必要的:
当 i 和 j 都匹配到下标为5的字符时,发现主串和字串的字符不匹配,BF算法在此时就会将i 回退到主串下标1字符b,j回退到子串0下标重新进行匹配,既然是匹配到最后一个字符才失败,那么 i 前面和 j 前面一定有一部分是相同的,这里相同部分就是主串0,1和3,4下标都是ab字符串,我们发现此时 j 回退到2下标c位置重新开始合适,i 直接不回退
区别: KMP 和 BF 唯一不一样的地方在,我主串的 i 并不会回退,并且 j 也不会移动到 0 号位置,而是回退到一个特殊的位置
2.图解演示:
3. 为什么主串 i 不回退?
在下面这种情况下,在下标2位置匹配失败,i 即使回退到1位置也是没有必要的,因为 i回退到1位置的字符b 和 子串下标0位置的字符a 也不一样
4. j 的怎么进行位置的回退——引出next数组
从上面KMP算法解决的问题可知:
此时匹配失败,我们不回退 i ,因为在这个地方匹配失败,说明 i 的前面和 j 的前面,是有部分是相同的,不然两个下标不可能走到这里来,所以 j 回退到2下标,i 不回退,这就是最好的情况
那么我们怎么知道 j 回退到哪个位置呢?由此引入了next数组
KMP 的精髓就是 next 数组: 这个数组用来保存某个位置匹配失败后,回退的位置
也就是用 next[ i ] = k来表示,不同的 i 来对应一个k值, 这个 k 就是你将来要移动的i要移动的位置
就拿上面的例子来说,j 回退到2下标 那么next数组中 next [ 5 ] = 2
而 K 的值是这样求的(求next数组):(1) 规则: 在子串中找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 开始,另一个以-1 下标结尾。
(2) 不管什么数据 next[0]= -1;next1]= 0;在这里,我们以下标来开始,而说到的第几个第几个是从 1 开始(也有些地方next[0]= 0;next1]= 1)同样以上面的子串 abcabc 为例,求他的next数组:
下标0和下标1是固定的,那就不用说
下标2 :j 处于下标2 ,我们就看有没有一个字符串 以下标0(a字符)开始 ,另一个字符串以下标 -1(b字符)结束 的两个相同的字符串 ab这三个字符中肯定没有 所以next [2] = 0
下标3:j 处于下标3 ,我们就看有没有一个字符串 以下标0(a字符)开始 ,另一个字符串以下标 -1(c字符)结束 的两个相同的字符串 abc这三个字符中肯定没有 所以next [3] = 0
下标4:j处于下标4,我们同样看 有没有一个字符串 以下标0(a字符)开始 ,另一个字符串以下标 -1(a字符)结束 的两个相同的字符串 abca这三个字符中是有相同字符串a的 所以next [4] = 1(这里的1代表相同字符串的长度,没有就为0)
下标5 :j处于下标5 abcab 中ab 为相同的(一个a开头 另一个b结尾)字符串 所以next [5] = 2
求next数组的练习: 跟上面的过程一样,如果不懂可以去看 博哥视频讲解的KMP算法 30min的位置
练习 1: 举例对于”ababcabcdabcde”,求其的 next 数组?
答案: -10012012001200
练习 2: 再对”abcabcabcabcdabcde”,求其的 next 数组?
答案: -10001 2345678901230
一般情况答案都是next[0]= 0;next1]= 1,所以我们在此答案基础上全部+1即可
从上面的答案我们可以得出结论:数组在增的时候都是一个一个+1,不可能跳着加
到这里大家对如何求next数组应该问题不大了,接下来的问题就是 :
5.已知next[ i ] = k;怎么求next[i+1]=?
如果我们能够通过 next [ i ]的值,通过一系列转换得到 next [ i+1]得值,那么我们就能够实现这部分
首先假设: next[ i ] = k 成立 (为了方便数组名命名为p)
那么,就有这个式子成立:p [ 0 ]...p [ k-1 ] = p [ x ] ..p [ i-1 ]
因为 i -1 -k = k -1 那么 x = i - k ,也就是p [ 0 ]...p [ k-1 ] = p [ i - k ] ..p [ i-1 ]
到这一步: 我们再假设如果 p [ k ] = p [ i ] ;在上面得到的式子两边加上这个式子
我们可以得到p [ 0 ]...p [ k ] = p [ i-k ] ..p [ i ] ;那这个就是 next[ i+1]= k+1;
那么: p[ i ] != p[ k ] 呢?
看如下实例:
一次不匹配 ,j 回退到 2下标位置 不一定是你要找的
继续回退 此时回退到了0下标 (也就是说 k一直回退 去找 p [i] == p [k] ,这样就满足了p [ k ] = p [ i ])
6.KMP算法代码实现
//找到子串在主串当中的下标public static int KMP(String str,String sub,int pos) {if(str == null||sub == null) return -1;int lenStr = str.length();int lenSub = sub.length();if(lenStr == 0||lenSub == 0) return -1;if(pos<0 || pos >= lenStr) return -1;int [] next = new int[lenSub];getNext(sub,next);int i = pos;//从pos位置开始遍历主串int j = 0;//遍历子串while(i < lenStr && j <lenSub) {//这里要考虑到一开始就不匹配,j=-1if (j==-1||str.charAt(i) == sub.charAt(j)) {i++;j++;} else {//下标不一样,一直回退j = next[j];}}if(j==lenSub) {return i-j;}return -1;}//重点:求子串的next数组public static void getNext(String sub,int [] next) {next[0] = -1;next[1] = 0;int i = 2;//i表示所求next数组的下标,是提前走了一步的int k = 0;//比较是否相等的前一项的k//这里next[i]就是要求的,和我们分析的next[i+1]一样// 原来判断的是p[i]==p[k],现在应该判断p[i-1]==p[k]while(i < sub.length()) {//此处要考虑k回退到了-1位置,next值就为0if (k==-1||sub.charAt(i-1) ==sub.charAt(k)) {next[i] = k+1;k++;i++;} else {//p[i-1]!=p[k],则k继续回退k = next[k];}}}
7.next数组的优化
为什么要对next数组进行优化?
有如下串:aaaaaaaab,他的 next 数组是-1,0,1,2,3,4,5,6,7
假设5位置匹配失败,那么就得回退到4位置,4位置和5位置都是a,那么还得回退到3位置,而3位置和4位置都是a,还得继续回退,就这样一直回退到0位置,由此引入了nextval数组进行了优化
next 数组的优化,即如何得到 nextval 数组:
(1)回退到的位置和当前字符一样,就写回退那个位置的nextval值
(2)如果回退到的位置和当前字符不一样,就写当前字符原来的next值
就以上面字符串为例:
0下标:肯定还是为-1
1下标:这个位置回退到0位置,因为这个位置的值和0位置(回退的位置)的值一样,所以这个位置的值就写回退位置的值(即-1)
2-7下标:这些位置回退到前一个位置,值都是一样的,所以都是-1
8下标: 回退到的位置和当前字符不一样,直接写next[ 8 ]的值7即可
则修正后的数组 nextval 是:-1, -1,-1,-1, -1, -1, -1, -1,7。
练习: 模式串 t='abgabbcabcaabdab’,该模式串的 next 数组的值为 ( D )nextva1 数组的值为 (F)
答案:在下面答案的基础上+1即可选择
这里也不做过多的解释,过程跟上面一样,不懂的可以评论区或者私信问我,或者 看博哥视频讲解的KMP算法 2h的位置
本次内容就到此啦,欢迎评论区或者私信交流,觉得笔者写的还可以,或者自己有些许收获的,麻烦铁汁们动动小手,给俺来个一键三连,万分感谢 !
相关文章:

【数据结构】字符串匹配|BF算法|KMP算法|next数组的优化
字符串匹配算法是在实际工程中经常遇到的问题,也是各大公司笔试面试的常考题目,本文主要介绍BF算法(最好想到的算法,也最好实现)和KMP算法(最经典的) 一、BF算法 BF算法,即暴力(Bru…...

阿里云 ACK One 新特性:多集群网关,帮您快速构建同城容灾系统
云布道师 近日,阿里云分布式云容器平台 ACK One[1]发布“多集群网关”[2](ACK One Multi-cluster Gateways)新特性,这是 ACK One 面向多云、多集群场景提供的云原生网关,用于对多集群南北向流量进行统一管理。 基于 …...

vscode自定义代码片段
前言 代码片段,指的是能够帮助输入重复代码模式,比如初始页面的模板。通过 snippet ,我们仅仅输入一小段字符串,就可以在代码片引擎的帮助下,生成预定义的模板代码,接着我们还可以通过在预定义的光标位置之…...

【贪心算法】专题练习一
欢迎来到Cefler的博客😁 🕌博客主页:那个传说中的man的主页 🏠个人专栏:题目解析 🌎推荐文章:题目大解析(3) 前言 1.什么是贪心算法?——贪婪鼠目寸光 贪心策…...

【JMeter】使用nmon进行性能资源监控
一、前言 在工作中可能会遇到需要在压测的时候对Linux服务器进行性能资源监控的情况。这时可以用nmon来对服务器进行监控。 二、nmon的下载安装 1.查看系统信息 shell cat /etc/os-release 结果为 shell PRETTY_NAME"Debian GNU/Linux 12 (bookworm)" NAME&…...

Unity预设体
目录 预设体是什么? 如何创建预设体? 如何修改预设体? 如何删除预设体? 预设体是什么? Unity中的预设体(Prefab)是一种可重复使用的游戏对象模板。它允许开发者创建一个或多个游戏对象&…...

Elasticsearch 写入优化探索:是什么影响了refresh 耗时?
1、问题背景: 数据写入后,refresh耗时过长,能达到1s-5s。 想通过测试,探索确认影响refresh的因素,比如:写入操作是新增还是更新,deleted文档占比是否有影响,是否有其他索引配置&…...
Java8新特性——函数式接口
目录 一、介绍 二、示例 (一)Consumer 源码解析 测试示例 (二)Comparator (三)Predicate 三、应用 四、总结 一、介绍 FunctionalInterface是一种信息注解类型,用于指明接口类型声明…...

Epson打印机连接wifi
环境 Epson L3153 打印机联通无线光猫 背景 最近家里的联通宽带不太稳定,经常断网。今天打了联通客服电话,师傅上门来,说可能是光猫用的时间太长了,换了一个新的联通光猫,问题解决。 wifi的名称是 CU_Y3ft 和 CU_Y3…...
Chapter 7 - 6. Congestion Management in Ethernet Storage Networks以太网存储网络的拥塞管理
Dedicated and Converged Ethernet Network专用和融合以太网网络 Just because a network is configured as a converged Ethernet network (lossy and lossless traffic), doesn’t necessarily mean that lossy and lossless traffic runs on it simultaneously. For exampl…...

【论文笔记】NeuRAD: Neural Rendering for Autonomous Driving
原文链接:https://arxiv.org/abs/2311.15260 1. 引言 神经辐射场(NeRF)应用在自动驾驶中,可以创建可编辑的场景数字克隆(可自由编辑视角和场景物体),以进行仿真。但目前的方法或者需要大量的训…...
通信原理 | 分贝dB、功率、功率谱、功率谱密度、信噪比
文章目录 分贝功率和分贝的关系能量谱功率谱功率谱和功率谱密度是不同的功率谱密度随机信号和确知信号信噪比基本定义分贝表示应用分贝 分贝:(用dB表示)是量度两个相同单位之间数量比例的计量单位,主要用于度量声音强度。 1贝尔(B)=10分布(dB),即1B = 10dB 分贝是以美国…...
Go中的Context是什么?
在 Go 编程语言(通常称为 Golang)中,术语 "上下文 "指的是上下文包及其定义的上下文类型。上下文包用于跨 API 边界和进程间传输截止日期、取消信号和其他请求范围值。 上下文包的主要目的是管理并发或分布式系统中操作的生命周期…...

碳排放预测 | 基于ARIMA和GM(1,1)的碳排放预测(Matlab)
目录 预测效果基本介绍模型描述ARIMA模型GM(1,1)模型 程序设计参考资料 预测效果 基本介绍 基于ARIMA和GM(1,1)的碳排放预测(Matlab) 基于ARIMA(自回归移动平均模型)和GM(1,1)(灰色预测模型)的碳排放预测是…...

FPFA.一种二倍频电路代码描述以及测量详情
一、前言 1、因为需要倍频电路所以找了个二倍频的电路,通过fpga实际测量发现经过倍频后的电路峰值降低。不过这个也正常,因为该电路只要过触发点就会开始发生波形变化,而电路的触发值不是峰值。 2、继续对电路做倍频后信号做二…...

dotnet命令创建C#项目,VSCode打开
在命令行中创建项目并运行 1.首先安装.net 下载地址:.NET | 构建。测试。部署。 2.在 cmd 控制台输入 dotnet --vesion 检查版本号是否正常 3.我用git bash环境输入命令创建项目 // 创建文件夹 mkdir MyVSCode // 进入该文件夹 cd MyVSCode/ // 创建控制台项目 dotnet …...

在GitHub找开源项目
在 GitHub 的搜索框里: 使用搜索关键词可以在 GitHub 上快速的找你需要的开源项目: 限制搜索范围 通过 in 关键词 (大小写不敏感) 限制搜索范围: 公式搜索范围in:name xxx项目名包含xxxin:description xxx项目描述包含xxxin:readme xxx项目…...

GAMES101-LAB1
文章目录 一、问题简述二、框架准备三、作业参考3.1 模型矩阵3.1 参考代码 3.2 投影矩阵3.2.1 压扁操作(透视投影)3.2.2 正交投影3.2.3 参考代码 四、附件 一、问题简述 接下来的三次作业,将模拟一个基于CPU的光栅化渲染器的简化版本本次作业的任务是实现一个旋转矩…...

Docker 编译OpenHarmony 4.0 release
一、背景介绍 1.1、环境配置 编译环境:Ubuntu 20.04OpenHarmony版本:4.0 release平台设备:RK3568 OpenHarmony 3.2更新至OpenHarmony 4.0后,公司服务器无法编译通过,总是在最后几十个文件时报错,错误码4000…...
Vue 3 表单处理精讲:打造响应式注册表单的艺术
🧙♂️ 诸位好,吾乃诸葛妙计,编程界之翘楚,代码之大师。算法如流水,逻辑如棋局。 📜 吾之笔记,内含诸般技术之秘诀。吾欲以此笔记,传授编程之道,助汝解技术难题。 &…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...