一分钟教你弄懂KMP算法

news2024/11/25 7:01:09

问题背景

KMP算法主要应用与字符串的比较,有一个主串,有一个子串,我们要通过一种方式来查看子串是否为主串的一部分。我们通常的想法是:主串和子串左对齐,一个字符一个字符进行比较,如果其中有个字符不匹配的话,那么主串不同,子串向右移动一个范围。这样不断循环,直到找到一个完整的匹配的子串。但如果这样做的话,无疑时间复杂度是最高的,时间复杂度是mn。那有没有更快的方式呢?KMP算法的特点就是一个快速,能够在这种情况下快速地找到一个匹配的子串。

KMP算法是什么呢?

KMP算法操作呢?这个时候就要引入一个公共前后缀和最大公共前后缀的概念,比如这样的一串字符“A B A B A B” 这样的一串字符有很多公共的前后缀,AB是一个ABAB也是一个,甚至没有限制的话ABABAB也能算一个。那最大公共前后缀呢?这里我们的最大公共前后缀实际上有一个限制,那就是小于自身长度的最大公共前后缀,这个时候的最大公共前后缀就是ABAB了。知道这个有什么用呢?我们知道在傻瓜式的比较方式中,每次比较完了之后,子串不仅要向右移动,其中代表子串数组下标用于移动的变量,我们暂且称之为指针(虽然不是真正意义上的),这个指针是需要回溯的,也就是回到子串的首部进行重新比较。这个时候我们就有一个让指针保持在原地的方式,那就是让子串移动到的公共前缀和公共后缀重叠的地方来,然后在指针的位置进行比较。那有些人会有疑惑:那你这样移动那么多位数的话,那中间是不是错过了匹配的情况呢?事实上是不会的,如果错过了,那一定是公共前后缀选择小了,即找的那个并不是真正的公共前后缀这个点就是这个KMP算法的核心,也就是这个算法的神奇之处。如果不太理解,记住即可,这个就是事实。

next数组

很好,我们有了基本的操作方式,可以高效率地判断这个子串是否是这个主串中的一部分了。如果我们止步于此,那么我们就只能手工地进行一些简单的判断,但是进入到代码层面,要考虑的事情还有很多。

首先我们面对的是各式各样的比较,在我们拿到一个主串和一个子串的时候,我们是不能预先知道这个子串在和主串比较的时候在那个元素上发生故障的,所以我们在操作的时候,每次都得计算最大公共前后缀是那个,然后才能移动,那有没有可能将这个过程封装起来,类似于一个函数,我们首先假设每一个位置都会失败(因为运行之前我们不知道哪个会失败,所以只能假设全都失败),然后进而求出他们的公共前后缀(公共前后缀只需要知道子串就好了),然后再计算他们移动的位数,这样的话,在真正的执行的过程中,那个位置比较失败了,就调用相关的函数将其中的移动的步数求出来,但是落实到代码当中,内存是不是真正移动的,所以只能是指针移动到子串的那个位置和此时出错的主串元素进行比较,这样话就转化成,某个位置失败了,我们就直接可以知道接下来那个子串的位置和我这个主串的位置进行比较了。那我们还可不可以将这个方式再优化一下,我们能不能提前执行这个函数,将这个位置结果存在一个固定的地方,下次直接调用就好了?是的,这个就是next数组!表示的就是,当某个位置失败的时候,那我们可以通过这个位置的下标访问这个数组,取到这个数组里面的值,这个值就是我们子串中下次和主串相比较的位置下标。

其中的next数组下表是从1开始的,下表0里面是不存东西的。next数组里面的值0表示的就是子串的第一个和主串失败位置的下一个进行比较。 

next数组的代码实现细节

既然有了实现next数组的思路,那具体的代码怎么实现呢?实现的时候要注意什么呢?如果我们以遍历的方式来求next数组的话,那么具体的过程是这样的:对于每一个子串失败的位置,我们都需要用两个指针在失败位置前的全部部分,一左一右进行扫描,跳出循环的条件是右边到达了最左边,左边到达了最右边,同时两个指针指向的内容进行比较,如果相同的话,继续走,同时计算个数c,如果不对的话跳出循环,这个时候第 c +1个子串的上的元素就要和主串上的元素进行比较,这样的下来的话,要走n趟,每一趟的复杂度也是n,总的来说的是n^2,这样的话就和不采用next数组的傻瓜方式的时间复杂度就是一样的了。那能不能优化呢?

我们在从子串的第一个元素到最后一个元素都在计算Next数组的值,值也是算一个放一个,那算后续Next的值的时候,能不能使用之前已经计算过的Next的值呢?答案是可以的。我们观察上一个部分的next数组的部分的时候,就发现了这样的一个规律:我们next数组中存的其实就是最大公共前后缀 + 1 后的数值,这个很重要!比如我们计算了第n个位置的next数组的值之后,想要计算n + 1个位置上的Next数组上的值,怎么计算呢?这个时候就得看最大公共前后缀!如果我们发现,子串中n上的值和n的失败过后找到的位置t上面的值是相同的话,那么这就意味着,子串上的第 n+ 1 个元素的最大公共前缀是比n的多一个的,这不就意味着,n + 1 在next数组中的值是 n在next数组中的值 + 1吗;如果不相等的话,这就有趣了,看下图!

如果不相等的话,这不就回到我们问题的初始阶段吗,可以将上述的子串看做是主串,下面的子串就是比较的子串。因为我们现在求得是最大公共前后缀,只需要子串就好了,不需要主串,因为我们求最大公共前后缀的前提就是假设某一个元素的失败的,已经过了和主串比较的阶段,所以这里只讨论子串。这个时候Pt和Pj(这里的j就是上述说的n)比较失败,那我们就得寻找子串 t 位置上的公共前后缀,我们有吗?肯定是有的,我们都讨论到了子串j上的Next数组的值,那小于j的t上的值是一定有的,这个时候直接查next数组就好了,然后再将这个值赋值和t,也就是t = next[t],有些人看这个代码有点不明白,是的,初看的时候确实有点模糊,心里想为什么不换一个变量名字区分一下呢?其实这样做也是为了重复利用变量t这部分的空间,如果还看不明白,就只要记住赋值号的结合性是从右向左的,而不是从左向右的,这就是为什么很疑惑的原因了,只要搞清楚了语法的具体执行步骤,先取出t所在next数组里面的值,再赋值给t,覆盖掉之前在t中的值,这个时候t就是一个新的t了,这样就不会疑惑了。这样不断寻找的话,只要找到了新的t中的值是和原来的j所在的值是相同的,那么 子串中j + 1所在位置失败的时候,寻找的位置就是 (新 t + 1),如果到最后还是没有找到的话,也就是说找到了子串的第一个进行比较还是失败的话,注意这里很容易被绕进去,现在我们讨论的是公共前后缀的个数,我们现在是计算 j +1的公共前后缀的个数(不要和正式的主串和子串的比较产生混淆),现在正在用j + 1前面 j 的值和子串中第一个值进行比较,只有第一个值失败之后,我们再回到next数组里面的才会有 t = 0,当出现t = 0的时候,这就意味着连第一个位置都不和 Pj是相同的,那么这就代表着   j + 1前面的所有部分是没有公共前后缀的,即为0,所以当 第j +1 个位置失败之后,是需要第一个元素来个主串比较的,不管是next数组的规律公式层面,还是理解层面,这个时候 j + 1在next数组里面需要填写的值是 0 + 1 或者是1. 所以才会出现next[j + 1] = 1; 

值得注意的是,在整个next数组中,只有子串中第一个元素失败的时候,其中的next值才是0,其代表的意思是:子串的第一个元素和主串第n个位置比较失败,这个时候,子串中已经没有可以比较的元素了,所以主串和子串的匹配开头元素一定不是从这个元素开始的,所以接下来的策略是将子串想后移动一位,让子串的第一个元素和主串的第n + 1个元素进行比较。

有了next数组就够了吗?还可以优化吗?不太够,可以优化。如果我们出现这样的情况:

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

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

相关文章

北京市经信局局长姜广智带队调研三六零 强调大模型应与行业结合

12月6日,北京市经济和信息化局局长姜广智、副局长王磊带队走访调研三六零集团,就共促城市级数字安全基础设施项目落地,打造引领行业发展标杆项目,推动大模型落地应用赋能产业、行业发展等话题进行交流。360集团创始人周鸿祎接待来…

h5和小程序通信

相信大部分人可能都会遇到要在微信小程序里面嵌入h5,这个时候h5和小程序之间的通信就成了不可避免的一环,不用紧张,其实很简单。 看一下微信小程序官方文档怎么说 首先我们按照文档上的指示,在需要向小程序传递参数的页面引入 jw…

从霸总短剧的热播,看出海品牌如何巧妙利用短剧进行全球推广

近期,中国式“霸总”短剧在国外走红,看着这熟悉的剧情模式和作品结构,让一众国内网友震惊的同时,也为中国品牌的全球推广带来了新的思路和灵感。本文Nox聚星将和大家从霸总短剧在海外的热播出发,探讨出海品牌如何巧妙利…

【动态规划】02斐波那契数列模型_三步问题(easy)

题目链接:leetcode三步问题 目录 题目解析: 算法原理: 1.状态表示 2.状态转移方程 3.初始化 4.填表顺序 5.返回值 编写代码: 题目解析: 题目让我们求小孩到达n阶台阶的时候,可以有多少上楼梯方式; 由题可得&a…

理解IO复用的三种模式——select、poll、epoll

文章目录 一、Select1、select简介2、select实现原理3、select编程4、select常见问题 二、poll1、poll简介2、poll实现原理3、poll编程4、poll常见问题 三、epoll1、epoll简介2、epoll实现原理3、epoll编程4、epoll常见问题 Linux IO模型是指Linux操作系统中用于实现输入输出的…

MySQL 包含查询特殊符号数据

当你模糊查询包含特殊符号的数据时,如果不加上特殊处理,查询结果是错误的。 如果你查的数据包含如上字符或者其他特殊字符,需要加上\转义字符。 如下示例: SELECT * FROM t_bc_user t where t.name LIKE %\_%

四、分代垃圾回收机制及垃圾回收算法

学习垃圾回收的意义 Java 与 C等语言最大的技术区别:自动化的垃圾回收机制(GC) 为什么要了解 GC 和内存分配策略 1、面试需要 2、GC 对应用的性能是有影响的; 3、写代码有好处 栈:栈中的生命周期是跟随线程&…

ncnn模型部署——使用VS2019把项目打包成DLL文件

一、项目打包成DLL文件 1.创建动态链接库DLL项目 创建完成,项目中包含源文件dllmain.cpp, pch.cpp,头文件framework.h, pch.h 2.编写和配置DLL项目 (1)配置pch.h文件,在头文件pch.h中定义宏,宏的作用的是…

VUE3给table的head添加popover筛选、时间去除时分秒、字符串替换某字符

1. VUE3给table的head添加popover筛选 <el-tableref"processTableRef"class"process-table"row-key"secuId":data"pagingData"style"width: 100%"highlight-current-row:height"stockListHeight":default-exp…

晶圆测试工艺介绍

第一章、晶圆测试简介 晶圆测试的方式&#xff0c;主要是通过测试机&#xff08;事先编好程序&#xff09;和探针台的联动&#xff0c;依靠探针卡的接触衔接&#xff0c;进行晶圆级的芯片测试。 当探针卡Probecard 的探针正确接触晶圆wafer 内一颗 芯片die的每个接触点bondpads…

毕设:《基于hive的音乐数据分析系统的设计与实现》

文章目录 环境启动一、爬取数据1.1、歌单信息1.2、每首歌前20条评论1.3、排行榜 二、搭建环境1.1、搭建JAVA1.2、配置hadoop1.3、配置Hadoop环境&#xff1a;YARN1.4、MYSQL1.5、HIVE(数据仓库)1.6、Sqoop&#xff08;关系数据库数据迁移&#xff09; 三、hadoop配置内存四、导…

网络层(1)——概述

一、概述 网络层毫无疑问是最复杂的一层&#xff0c;涉及到大量的协议与结构的内容。在如今主流的设计中&#xff0c;大家都会把网络层分成两个部分&#xff1a;数据平面、控制平面。其中数据平面指的是网络层中每台路由器的功能&#xff0c;它决定了到达路由器端口输入链路之一…

软考2018下午第六题改编逻辑(状态模式)

在状态模式中&#xff0c;我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象 package org.example.状态模式.软考航空;/*** author lst* date 2023年12月07日 15:37*/ class FrequentFlyer {CState state;double flyMiles;public FrequentFlyer() {…

为什么 AWS 数据库不讲 HTAP

在 AWS re:Invent 2023 掌门人 Adam Selipsky 的 Keynote 上&#xff0c;数据库方面最重磅的主题是 Zero-ETL&#xff0c;从 TP 数据库 (RDS, Aurora, DynamoDB) 同步数据到 AP 数据库 (Redshift)。 Zero-ETL 是 AWS 在去年 re:invent 2022 上推出的概念&#xff0c;今年则继…

Peter算法小课堂—贪心算法

课前思考&#xff1a;贪心是什么&#xff1f;贪心如何“贪”&#xff1f; 课前小视频&#xff1a;什么是贪心算法 - 知乎 (zhihu.com) 贪心 贪心是一种寻找最优解问题的常用方法。 贪心一般将求解过程分拆成若干个步骤&#xff0c;自顶向下&#xff0c;解决问题 太戈编程第…

simulink enable模块——使能子系统案例仿真分析

1.案例分析 仍以一个简单的乘法增益案例分析 分析&#xff1a;可以看到&#xff0c;在满足条件性才条用使能子系统&#xff0c;在t1s和3s时刻&#xff0c;进行增益操作&#xff0c;这和上篇博客中的触发trigger子系统相同的作用。 simulink trigger模块使用——多种调用案例分…

膜结构建筑:未来体育可持续发展的绿色引擎

随着城市化的飞速发展&#xff0c;现代建筑迫切需要创新性的解决方案&#xff0c;而膜结构建筑以其独特的设计理念和可持续性特点&#xff0c;正在成为未来城市发展的重要引擎。本文将深入探讨膜结构建筑在可持续城市发展中的关键作用&#xff0c;包括其在节能减排、资源有效利…

【Qt开发流程】之元对象系统

描述 Qt的元对象系统&#xff08;Meta-Object System&#xff09;是Qt框架的核心机制之一&#xff0c;它提供了运行时类型信息&#xff08;RTTI&#xff09;和信号与槽&#xff08;Signals and Slots&#xff09;机制的支持。元对象系统在Qt中扮演了很重要的角色&#xff0c;它…

C++STL的string类(一)

文章目录 前言C语言的字符串 stringstring类的常用接口string类的常见构造string (const string& str);string (const string& str, size_t pos, size_t len npos); capacitysize和lengthreserveresizeresize可以删除数据 modify尾插插入字符插入字符串 inserterasere…

解决WPS拖动整行的操作

如上图&#xff0c;想要把第4行的整行内容&#xff0c;平移到第1行。 1.选中第4行的整行 2.鼠标出现如图的样子时&#xff0c;按住鼠标左键&#xff0c;上移到第1行位置后&#xff0c;放开左键即可。