数据结构 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.mzph.cn/news/899659.shtml

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

相关文章

Conda配置Python环境

1. 安装 Conda 选择发行版&#xff1a; Anaconda&#xff1a;适合需要预装大量科学计算包的用户&#xff08;体积较大&#xff09;。 Miniconda&#xff1a;轻量版&#xff0c;仅包含 Conda 和 Python&#xff08;推荐自行安装所需包&#xff09;。 验证安装&#xff1a; co…

数仓开发那些事(11)

某神州优秀员工&#xff1a;一闪&#xff0c;领导说要给我涨米。 一闪&#xff1a;。。。。&#xff08;着急的团团转&#xff09; 老运维&#xff1a;Oi&#xff0c;两个吊毛&#xff0c;看看你们的hadoop集群&#xff0c;健康度30分&#xff0c;怎么还在抽思谋克&#xff1f…

MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案

✅ MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案 前言一、问题现象二、原因分析1. 使用了 strictInsertFill/strictUpdateFill 导致更新失效2. 实体类注解配置错误3. MetaObjectHandler 未生效4. 使用自定义 SQL 导致自动填充失效5. 字段类型不匹配 三、…

C++ STL常用算法之常用算术生成算法

常用算术生成算法 学习目标: 掌握常用的算术生成算法 注意: 算术生成算法属于小型算法&#xff0c;使用时包含的头文件为 #include <numeric> 算法简介: accumulate // 计算容器元素累计总和 fill // 向容器中添加元素 accumulate 功能描述: 计算区间内容器元素…

axios基础入门教程

一、axios 简介 axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;可用于浏览器和 Node.js 环境&#xff0c;支持以下特性&#xff1a; 发送 HTTP 请求&#xff08;GET/POST/PUT/DELETE 等&#xff09; 拦截请求和响应 自动转换 JSON 数据 取消请求 并发请求处理 二…

短视频团队架构工作流程---2025.3.30 李劭卓

短视频团队架构&工作流程—2025.3.30 李劭卓 文章目录 短视频团队架构&工作流程---2025.3.30 李劭卓1 工作职责1.1 编剧&#xff1a;1.2 主编&#xff1a;1.3 总编&#xff1a;1.4 导演&#xff1a;1.5 摄影&#xff1a;1.6 演员&#xff1a;1.7 后期&#xff1a;1.8 美…

MySQL 高效 SQL 使用技巧详解

MySQL 高效 SQL 使用 技巧详解 一、为什么需要优化 SQL&#xff1f; 性能瓶颈&#xff1a;慢查询导致数据库负载升高&#xff0c;响应时间延长。资源浪费&#xff1a;低效 SQL 可能占用大量 CPU、内存和磁盘 I/O。 目标&#xff1a;通过优化 SQL 将查询性能提升 10 倍以上&am…

AI基础03-视频数据采集

上篇文章我们学习了图片的数据采集&#xff0c;今天主要了解一下视频数据采集的方法。视频是由一系列图像构成的&#xff0c;其中每一张图片就是一帧。视频数据采集方法通常有自动图像采集和基于处理器的图像采集两种。我们学习一下如何利用python 工具和笔记本计算机摄像头进行…

Scala 数组

Scala 数组 引言 Scala 作为一门多范式编程语言&#xff0c;融合了面向对象和函数式编程的特点。数组是编程语言中非常基础和常见的数据结构&#xff0c;在 Scala 中也不例外。本文将详细介绍 Scala 中的数组&#xff0c;包括其定义、操作以及在实际开发中的应用。 Scala 数…

Text-to-SQL将自然语言转换为数据库查询语句

有关Text-To-SQL方法&#xff0c;可以查阅我的另一篇文章&#xff0c;Text-to-SQL方法研究 直接与数据库对话-text2sql Text2sql就是把文本转换为sql语言&#xff0c;这段时间公司有这方面的需求&#xff0c;调研了一下市面上text2sql的方法&#xff0c;比如阿里的Chat2DB,麻…

golang 的strconv包常用方法

目录 1. 字符串与整数的转换 2. 字符串与浮点数的转换 3. 布尔值的转换 4. 字符串的转义 5. 补充&#xff1a;rune 类型的使用 方法功能详解 代码示例&#xff1a; 1. 字符串与整数的转换 方法名称功能描述示例Atoi将字符串转换为十进制整数。strconv.Atoi("123&q…

MATLAB详细图文安装教程(附安装包)

前言 MATLAB&#xff08;Matrix Laboratory&#xff09;是由MathWorks公司开发的一款高性能的编程语言和交互式环境&#xff0c;主要用于数值计算、数据分析和算法开发。内置数学函数和工具箱丰富&#xff0c;开发效率高&#xff0c;特别适合矩阵运算和领域特定问题。接下来就…

ShapeCrawler:.NET开发者的PPTX操控魔法

引言 在当今的软件开发领域&#xff0c;随着数据可视化和信息展示需求的不断增长&#xff0c;处理 PPTX 文件的场景日益频繁。无论是自动化生成报告、批量制作演示文稿&#xff0c;还是对现有 PPT 进行内容更新与格式调整&#xff0c;开发者都需要高效的工具来完成这些任务。传…

HTML5贪吃蛇游戏开发经验分享

HTML5贪吃蛇游戏开发经验分享 这里写目录标题 HTML5贪吃蛇游戏开发经验分享项目介绍技术栈核心功能实现1. 游戏初始化2. 蛇的移动控制3. 碰撞检测4. 食物生成 开发心得项目收获后续优化方向结语 项目介绍 在这个项目中&#xff0c;我使用HTML5 Canvas和原生JavaScript实现了一…

有关pip与conda的介绍

Conda vs. Pip vs. Virtualenv 命令对比 任务Conda 命令Pip 命令Virtualenv 命令安装包conda install $PACKAGE_NAMEpip install $PACKAGE_NAMEX更新包conda update --name $ENVIRONMENT_NAME $PACKAGE_NAMEpip install --upgrade $PACKAGE_NAMEX更新包管理器conda update con…

【Linux】调试器——gdb使用

目录 一、预备知识 二、常用指令 三、调试技巧 &#xff08;一&#xff09;监视变量的变化指令 watch &#xff08;二&#xff09;更改指定变量的值 set var 正文 一、预备知识 程序的发布形式有两种&#xff0c;debug和release模式&#xff0c;Linux gcc/g出来的二进制…

【Ubuntu常用命令】

1.将本地服务器文件或文件夹传输到远程服务器 文件 scp /data/a.txt administrator10.60.51.20:/home/administrator/ 文件夹 scp -r /data/ administrator10.60.51.20:/home/administrator/ 2.从远程服务器传输文件到本地服务器 scp administrator10.60.51.20:/data/a.txt /h…

golang 的time包的常用方法

目录 time 包方法总结 类型 time.Time 的方法 库函数 代码示例&#xff1a; time 包方法总结 类型 time.Time 的方法 方法名描述示例               ẵNow()获取当前时间和日期time.Now()Format()格式化时间为字符串time.Now().Format("2006-01-02 15…

Elasticsearch:使用 Azure AI 文档智能解析 PDF 文本和表格数据

作者&#xff1a;来自 Elastic James Williams 了解如何使用 Azure AI 文档智能解析包含文本和表格数据的 PDF 文档。 Azure AI 文档智能是一个强大的工具&#xff0c;用于从 PDF 中提取结构化数据。它可以有效地提取文本和表格数据。提取的数据可以索引到 Elastic Cloud Serve…

【ArcGIS操作】ArcGIS 进行空间聚类分析

ArcGIS 是一个强大的地理信息系统&#xff08;GIS&#xff09;软件&#xff0c;主要用于地理数据的存储、分析、可视化和制图 启动 ArcMap 在 Windows 中&#xff0c;点击“开始”菜单&#xff0c;找到 ArcGIS文件夹&#xff0c;然后点击 ArcMap 添加数据 添加数据 - 点击工具…