数据结构 KMP 字符串匹配算法

KMP算法是计算机科学中的一种字符串匹配算法,KMP是三个创始人名字首字母

题目

AcWing - 算法基础课

前置知识点

KMP算法是一种高效的字符串匹配算法,算法名称取自于三位共同发明人名字的首字母组合。该算法的主要使用场景就是在字符串(也叫主串)中的模式串(也叫子串)定位问题,常见的有“求子串出现的起始位置”、“求子串的出现次数”等。

前缀和后缀

假设一个字符串“ababa"

他的前缀就是a,ab,aba,abab (ababa,ababa,ababa,ababa)

他的后缀就是a,ba,aba,baba (ababa,ababa,ababa,ababa

前缀和后缀的最大长度,是原字符串长度-1

思路

最浅显易懂的 KMP 算法讲解_哔哩哔哩_bilibili

F03【模板】KMP 算法——信息学竞赛算法_哔哩哔哩_bilibili

给定一个主串S,模式串P,求P在S中出现的位置

普通暴力枚举

一个字符一个字符的双指针枚举,一旦发现不同

主串又要从第二个开始匹配,子串要从头开始匹配

缺点:时间复杂度太高

kmp优化思路

在比对失败时,我们已经知道曾经读过哪些字符了

已经匹配的子串,有相同的后缀, 前缀,那我们是不是可以保留相同的前缀,再去查找不同的剩下的字符串

如下图,主串保留的绿色,实际上是匹配的子串的后缀,子串里保留的绿色部分,就是匹配的子串的前缀

他们是相同的,则可以把主串里匹配的子串的后缀,视为子串前缀,继续在主串查找相同前缀后面的字符串

next数组使用

我们已经知道kmp的优化思路,那么如何将知道,该保留的前缀呢?再暴力双指针循环太麻烦

kmp三个人提出了next数组,我们先不看如何实现,先看如何使用,next数组功能

下图是一个初始状态

kmp算法匹配失败时,去看最后一个匹配成功的子串的字符,对应的next数组里的值

查到对应的值后,我们移动子串,跳过next数组里的值对应的字符个数,例如值是2,我们就跳过前两个字符

我们发现,跳过的这两个字符,确实能和主串指针指向位置前的两个字符对应

所以继续枚举即可,不需要从头枚举

这样,我们永远不需要回退主串指针,一次遍历主串,就可以找到匹配子串

再看一下 ,如果子串对应next是0时

如果为0,也是从头开始匹配子串,没有相同的前缀和后缀,则子串一定不在已经遍历过的主串里有

则已经遍历过的主串里的字符,一定没有子串的子串,所以主串之前的部分可以直接舍弃,不用移动主串指针

推导next数组

next数组中的值,实际上就是,子串以当前字符串结尾,在当前字符串中,最长的前缀,后缀相同的字符串长度

如下图

ABAB最长的相同前缀,后缀,该前缀或者后缀的长度是2

我们现在知道next的数组里的值代表什么了

那我们想一下,如何推导出next数组里的值

在后缀下一个字符,和前缀后一个字符相同时,我们只需要把next数组对应的值+1,然后填入即可

那在不同时,应该怎么办,循环遍历可以,但是时间复杂度较高

举例,上图再往下走一个

C和B不同,如何求B的对应的next值

下位字符不同,没办法构成更长的相同前后缀,我们看看有没有更短的前后缀

我们可以发现,确实存在更短的,两位的前后缀,但是这步在程序中如何体现,暴力求解似乎时间复杂度有点高

其实我们next数组里还有信息,也就是next[i]=3,则子串前3个数,和i-3到i,这两个字符串是相同的

字符串前三个字符,和i-3到i,就是前后缀,前三个数,和后三个数

也就是右边的后缀,其实等于左边的前缀,那我们其实可以忽略中间其他的值,直接去找到最左边

为什么不会有漏?第一,右边的后缀和左边的前缀完全相等

第二,后缀第四个字符和前缀第四个字符不同,所以不会比右边的后缀,或左边的前缀更长,也就不会用更多字符

这样,就相当于是求ABAB的前后缀

第三个A对应的值是1,B是字符串ABAB后缀的第二个字符(因为A对应的是1)

比较B和前缀的第二个字符是否相等

相等,则在B所处位置对应的next数组的值+1

B对应的next的数组的上一位的值,是通过第三个A获取的,但是实际上,和他组成后缀的是倒数第二个A

实际上是这样得到的next对应的值,抽象上是和第三个A组成的前后缀

因为倒数第二个A,对应的next的值为3,也就是和前三个字符前缀完全相等

所以可以直接拿第二个A的next计算,因为前三个字符,和后三个字符(这说的字符串next=3对应的A)完全一样

前三个有的字符,后三个都有,所以B可以和后三个字符组成的后缀,和前三个字符也可以组成相同的后缀

后三个字符代表的next值代表的前后缀相同的长度又太长,利用前三个字符代表的前后缀长度,即可完成回退

代码实现

next[i]代表子串p[1,i]相同前后缀的最大长度

i代表的指针,永远不往前回退,指向的是后缀的最后一个字符

j代表的指针,可以通过next数组回退,指向的是前缀最后一个字符

注:next在c++中有关键字,所以使用ne[]

next数组推导实现

ne[1]=0;
//两个字符串数组实际上都是从1开始
//i等于2是因为指向第二个字符即可,至少两个字符才有前后缀
//j=0,因为j从j+1开始比,也就是j=0是为了j从1开始
for(int i=2,j=0;i<=n;i++){                                                                                                   //j不为0,为0表示回到的子串第一个数的位置//i代表目前指到的位置,j代表相同前后缀的前缀的最后一个字符的位置//如果下位字符不同时,回退到j的值代表的前ne[j]位//上图中说前缀后缀完全相等,j回到相同的前缀的最后一个字符的位置//看ne[j]的下一位p[j+],和i指向的字符是否相等//相等结束循环,否则继续,直到相等或者j=0(回退到第一个字符串)while(j&&p[i]!=p[j+1])j=ne[j];//如果是相等,而不是j回退到了0,则j++,表示长度+1//j的值是ne[j]的值,j++=  就是  j=ne[j]+1,if(p[i]==p[j+1])j++;//记录下j的值,j现在指向的是相同前后缀的前缀的最后一个字符//代表的值是最长相同前后缀长度//把这个值加在ne[i]指针上,指向的是相同前后缀后缀的最后一个字符ne[i]=j;
} 

子串位置查找实现,查找主串出现子串的起始位置

 //推导子串出现位置,这次i等于1是因为,此循环判断的不是前后缀相同,而是判断是否为子串//因为两个完全一样的字符串,也互相为子串,子串第一个数有可能从i=1,j=1就开始了//所以这里要令i=1for(int i=1,j=0;i<=m;i++){//跳跃式找到主串现在指向的值,在子串中存在的位置while(j&&s[i]!=p[j+1])j=ne[j];//i指向主串的字符,和子串的字符下一个字符相等,则可以再推进走一步if(s[i]==p[j+1])j++;if(j==n)//长度一致,找到子串{cout<<i-j<<" ";//返回子串起始位置//可省略,意思是将j回退到除本身外,最大的相同前后缀长度,避免下个循环j+1越界j=ne[j];}}

整合代码

AcWing - 算法基础课

#include<iostream>
using namespace std;
const int N = 100010;
char p[N],s[N*10];
int ne[N];
int n,m;
int main(){cin >> n >> p + 1 >> m >> s + 1;ne[1]=0;//推导nxet数组for(int i=2,j=0;i<=n;i++){while(j&&p[i]!=p[j+1])j=ne[j];if(p[i]==p[j+1])j++;ne[i]=j;}//推导子串出现位置for(int i=1,j=0;i<=m;i++){//跳跃式找到主串现在指向的值,在子串中存在的位置while(j&&s[i]!=p[j+1])j=ne[j];if(s[i]==p[j+1])j++;if(j==n)//长度一致,找到子串{cout<<i-j<<" ";//返回子串起始位置//可省略,意思是将j回退到除本身外,最大的相同前后缀长度,避免下个循环j+1越界j=ne[j];}}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

06.AI搭建preparationの(transformers02)bertmodel实现bert-base-chinese的编码

一、下载 google-bert/bert-base-chinese at main 二、简介&#xff1a; 该模型的主要作用是获取每个汉字的向量表示&#xff0c;后续通过微调可应用于各种简体和繁体中文任务。 三、环境与设备&#xff1a; pycharm:2024 torch:2.2.0cu118 tensorflow2.6.0 python:3.9 tran…

JavaScript创建时间对象、数字、字符串方法

时间对象 四种时间创建方法 // 普通创建 var myData new Date(); console.log(myData);// 传参创建 年月日 时分秒 毫秒 var myData1 new Date(2019,10,1,9,10,20,30); console.log(myData1);// 字符串创建 年月日 时分秒 毫秒 var myData2 new Date("2019/10/1 9:10…

时序数据库:InfluxDB命令行操作

学习 InfluxDB 的命令行操作至关重要&#xff0c;它不仅是与数据库直接交互的工具&#xff0c;也是理解 InfluxDB 核心概念的关键途径。通过命令行&#xff0c;用户可以高效地执行数据库管理、数据查询和插入等任务&#xff0c;深入掌握 InfluxQL 的语法及功能。这对于调试、快…

基于MCU实现的电机转速精确控制方案:软件设计与实现

本文将详细介绍一篇基于微控制器&#xff08;MCU&#xff09;的电机转速精确控制的软件方案。通过采样PWM信号控制和ADC采样技术&#xff0c;结合PID闭环控制算法&#xff0c;实现了电机转速的高效、稳定调节。以下是软件方案流程图&#xff0c;下文将对其进行展开讲解。 原图太…

DeepSeek本地部署(linux)

一、下载并安装Ollama 1.下载Ollama Ollama官网:Ollama 点击"Download",会跳转至下载页面。 1.1在线下载安装 可复制此命令到Linux服务器进行在线下载,如下载速度过慢,可选择离线下载安装。 curl -fsSL https://ollama.com/install.sh | sh1.2离线下载安装 …

UE中不同摄像机震动的区别Camera Shake

Play World Camera Shake 和 Start Camera Shake 都用于触发摄像机震动效果&#xff0c;但它们的适用场景和实现方式有所不同。 1. Play World Camera Shake 功能&#xff1a; 在 世界中的某个位置 触发摄像机震动&#xff0c;震动效果会根据 摄像机与目标位置的距离 产生衰减&…

操作系统——线程的概念和特点

什么是线程&#xff0c;为什么要引入线程&#xff1f; 在很久以前&#xff0c;还没有引入进程&#xff0c;系统中各个程序只能串行执行 所以在那个时候&#xff0c;我们想一边运行音乐&#xff0c;一边运行QQ&#xff0c;显然是不可以实现的&#xff0c;在那个时候我们不可能…

大模型架构记录13【hr agent】

一 Function calling 函数调用 from dotenv import load_dotenv, find_dotenvload_dotenv(find_dotenv())from openai import OpenAI import jsonclient OpenAI()# Example dummy function hard coded to return the same weather # In production, this could be your back…

mysql5.7无法启动报错处理无日志

注意&#xff0c;本篇适用于mysql安装启动异常&#xff0c;而不是数据库本身的异常。所以在/var/log/mysql/下没有日志。 journalctl -u mysqld -n 15 查看启动日志&#xff0c;提示缺少共享库libaio.so.1 Mar 26 16:47:01 iZbp19v3umnf1z4en78hnlZ systemd[1]: Starting MyS…

android11关机安卓充电的UI定制化

引言 首先上一张安卓充电的图片&#xff1a; 安卓关机状态下有两种充电模式&#xff1a;uboot-charge和android-charge&#xff0c;可通过dts配置使用哪一种充电模式。 dts配置中uboot-charge和android-charge是互斥的&#xff0c;如下配置的是开启android-charge&#xff1a;…

忘记海康网络摄像机IP

海康网络摄像机的使用&#xff1a; 海康网络摄像机的使用 解决电脑无法通过网线直连海康摄像机的问题 使用vlc显示海康网络摄像机的视频 忘记海康网络摄像机IP 一、引言 如果忘记了海康网络摄像机的IP&#xff0c;可以通过下载海康的设备网络搜索软件“SADP”解决。 二…

【CSS3】04-标准流 + 浮动 + flex布局

本文介绍浮动与flex布局。 目录 1. 标准流 2. 浮动 2.1 基本使用 特点 脱标 2.2 清除浮动 2.2.1 额外标签法 2.2.2 单伪元素法 2.2.3 双伪元素法(推荐) 2.2.4 overflow(最简单) 3. flex布局 3.1 组成 3.2 主轴与侧轴对齐方式 3.2.1 主轴 3.2.2 侧轴 3.3 修改主…

百度自动驾驶:我的学习笔记

自动驾驶新人之旅(9.0版) 第一课&#xff1a;初识自动驾驶技术 1. 自动驾驶技术概述 2. 自动驾驶人才需求与挑战 3. 如何使用Apollo学习自动驾驶[上机学习] 4. 如何使用Apollo学习自动驾驶[上车学习] 第二课&#xff1a;入门自动驾驶技术 1. Apollo车云研发流程 2. Lin…

并发编程之FutureTask.get()阻塞陷阱:深度解析线程池CPU飚高问题排查与解决方案

FutureTask.get方法阻塞陷阱&#xff1a;深度解析线程池CPU飚高问题排查与解决方法 FutureTask.get()方法阻塞陷阱&#xff1a;深度解析线程池CPU飚高问题排查与解决方法1、情景复现1.1 线程池工作原理1.2 业务场景模拟1.3 运行结果1.4 发现问题&#xff1a;线程池没有被关闭1.…

记录vite引入sass预编译报错error during build: [vite:css] [sass] Undefined variable.问题

vite.config.ts resolve: {alias: {: path.resolve(__dirname, src),},},css: {// css预处理器preprocessorOptions: {scss: {additionalData: use "/assets/styles/block.scss" as *;,}}},block.scss $colorGreen: #00ff00;index.vue :v-deep .font-size-14{colo…

代码小练习

public class Test3 {public static void main(String[] args) throws ParseException {ArrayList<Integer> listnew ArrayList<>();Scanner scnew Scanner(System.in);while (true){System.out.println("请输入一个整数");String s sc.nextLine();int…

百人会上的蔚小理与「来的刚刚好」的雷军

这就是2025百人会上的蔚小理&#xff0c;努力的李斌、宣扬飞行汽车的何小鹏与大讲开源的李想。那么小米汽车的模式是什么呢&#xff1f;站在蔚小理的肩上。 这就是2025百人会上的蔚小理&#xff0c;努力的李斌、宣扬飞行汽车的何小鹏与大讲开源的李想。那么小米汽车的模式是什么…

日程公布| 第八届地球空间大数据与云计算前沿大会与集中学习(3号通知)

日程公布| 第八届地球空间大数据与云计算前沿大会与集中学习&#xff08;3号通知&#xff09; 日程公布| 第八届地球空间大数据与云计算前沿大会与集中学习&#xff08;3号通知&#xff09;

<em>赚</em><em>钱</em><em>彩</em><em>票</em><em>软</em><em>件</em>

&#xff1c;em&#xff1e;赚&#xff1c;/em&#xff1e;&#xff1c;em&#xff1e;钱&#xff1c;/em&#xff1e;&#xff1c;em&#xff1e;彩&#xff1c;/em&#xff1e;&#xff1c;em&#xff1e;票&#xff1c;/em&#xff1e;&#xff1c;em&#xff1e;软&#xf…

随机2级域名引导页HTML源码

源码介绍 随机2级域名引导页HTML源码,每次点进去都随机一个域名前缀。 修改跳转域名在 350 行代码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行 效果预览 源码免费获取 随机2级域名引导页…