【详解】KMP算法——每步配图让你打穿KMP

介绍

什么是KMP算法:

KMP算法主要运用串的模式匹配中(简单来说就是在s串中找到一个与t串相等的子串,称为模式匹配)例如s为abcdef,t为bcd,那么就是在s中找到bcd,并返回其在s中的首下标,该算法和BF算法相比有比较大的改进,主要是消除了主串指针的回溯,从而使算法的效率有了某种程度的提高。(了解这些就够了,其他都是废话)

有些读者可能不了解什么使BF算法,下面我简单讲解一下(了解的读者可跳过)

BF算法

可以看到BF算法其实就是类似遍历(一定可行,但是时间复杂度是O(m*n)m为s的字符个数,n为t的字符个数)效率太低了,KMP算法就是对它的改进

思维图如下:

代码如下(解释都在代码里了)

#define  _CRT_SECURE_NO_WARNINGS 1
//BF算法
#include <stdio.h>
#include <string.h>
typedef struct
{char data[1000];int length;
}SqString;
int index(SqString s, SqString t)
{int i = 0, j = 0;while (i < s.length && j < t.length){if (s.data[i] == t.data[j])  	//继续匹配下一个字符{i++;				//主串和子串依次匹配下一个字符j++;}else          			//主串、子串指针回溯重新开始下一次匹配{i = i - j + 1;			//主串从下一个位置开始匹配j = 0; 				//子串从头开始匹配}}if (j >= t.length)return(i - t.length);  		//返回匹配的第一个字符的下标elsereturn(-1);        		//模式匹配不成功
}
int main()
{SqString s, t;strcpy(s.data, "ababcabcacbab");strcpy(t.data, "abcac");s.length = 13;t.length = 5;printf("位置:%d\n", index(s, t));return 1;
}

接着就是我们的重头戏KMP算法,了解了BF算法,我们发现每次比较完,若不匹配,指向s的指针要进行回溯,那有没有办法让指向s的指针不回溯,一直向前这样是不是效率就会高很多。

KMP算法

分为两步

第一步:求next数组(是关于t串的数组)

我先给出代码(如果理解这个代码直接看第二步)

一开始看不懂很正常(因为非常抽象,花掉一个早上理解这个代码都算正常的)

求解next数组代码如下(别小看这个和递归一样小小代码,大大能量)

void GetNext(SqString t, int next[])
{int j, k;j = 0, k = -1;next[0] = -1;while (j < t.length - 1){if (k == -1 || t.data[j] == t.data[k]){k++; j++;next[j] = k;}else k = next[k];}
}

为什么要求next数组呢,这是因为KMP算法比较过程其实是让t串进行移动(理解这个非常重要),next数组存储当前可以移动多少个字符。

概念:对于t[j]的每个字符存在一个整数k,使得模式串t中开头的k个字符(t[0],t[1],...,t[k-1])依次与t[j]的前面k个字符(t[j-k],t[j-k+1]....t[j-1],这里t[j-k]最多从t[1]开始)。如果这样的k有多个取最大的一个,模式串t中每个位置j的字符都有这种信息,采用next[j]数组表示,及next[j] = MAX(k).

概念如上,不理解很正常下面我会用图画来帮助大家理解(包括我从不懂到理解的一些经验)

 首先总体next我先给出下面会逐一分析

总体next(不懂没关系看下面的)

规定next[0]为-1,这是规定不必深究,B前面只有A,故next[1]为0

找k的规则,大一点的看的比较清楚,我就先讲为什么下标7的B下面的k为什么为3,找的时候一个必须从0开始,另一个的结尾必须是下标7的前一个,7下标对应的k是反应前6个,不包括本身。

其中特别注意(从0开始的哪个不能超过j,从j前面为结尾的不能往前到0),这其实就是前后缀的概念(防止太乱我给了两张图片)

第一步:

next【0】规定为-1,next【1】前面只有A显然为0自己本身不算相等,next【2】要记得我上面讲的前后缀

第二步:


后面类似,就麻烦读者自行完成。

将上面的图解归纳起来就得到了一个求解next数组的公式

下面那个代码再理解理解(上是手算,下面是代码实现要理解一下,特别注意k每次最多增加1,k=next【k】是k回退,因为不相等,读者可以自己画图帮助理解一下)

void GetNext(SqString t, int next[])
{int j, k;j = 0, k = -1;next[0] = -1;while (j < t.length - 1){if (k == -1 || t.data[j] == t.data[k]){k++; j++;next[j] = k;}else k = next[k];}
}

接着就是

第二步KMP算法的模式匹配过程

一样我先给代码

int KMPIndex(SqString s, SqString t)
{int i = 0, j = 0;int next[20];GetNext(t, next);while (i < s.length && j < t.length){if (j == -1 || s.data[i] == t.data[j]){j++; i++;}else j = next[j];}if (j >= t.length) return (i-t.length);else return -1;
}

有了next数组的帮助,我们就可以知道当模式串t与s串不同时,t串要向右移动多少个字符 

整个过程图解如下:


相信细心的同学发现KMP算法其实是移动t串,s串是一直向前的。

总代码如下

c语言

#define  _CRT_SECURE_NO_WARNINGS 1
//串的模式匹配
//kmp求解
#include <stdio.h>
#include <string.h>
typedef struct
{char data[1000];int length;
}SqString;
void GetNext(SqString t, int next[])
{int j, k;j = 0, k = -1;next[0] = -1;while (j < t.length - 1){if (k == -1 || t.data[j] == t.data[k]){k++; j++;next[j] = k;}else k = next[k];}
}
int KMPIndex(SqString s, SqString t)
{int i = 0, j = 0;int next[20];GetNext(t, next);while (i < s.length && j < t.length){if (j == -1 || s.data[i] == t.data[j]){j++; i++;}else j = next[j];}if (j >= t.length) return (i - t.length);else return -1;
}
int main()
{SqString s1, s2;strcpy(s1.data, "abcdefg");strcpy(s2.data, "de");s1.length = 7;s2.length = 2;int t = KMPIndex(s1, s2);printf("%d", t);return 0;
}

c++版的如下

//KMP算法
#include "sqstring.cpp"
void GetNext(SqString t,int next[])		//由模式串t求出next值
{int j,k;j=0;k=-1;next[0]=-1;while (j<t.length-1) {	if (k==-1 || t.data[j]==t.data[k]) 	//k为-1或比较的字符相等时{printf("(1)j=%d,k=%d,两个指向字符相同",j,k);printf(",执行j++,k++ -> ");j++;k++;next[j]=k;printf("(1) j=%d,k=%d,next[%d]=%d\n",j,k,j,k);}else{printf("(2)j=%d,k=next[%d]=",j,k);k=next[k];printf("%d\n",k);}}
}
int KMPIndex(SqString s,SqString t)  //KMP算法
{int next[MaxSize],i=0,j=0;GetNext(t,next);while (i<s.length && j<t.length) {if (j==-1 || s.data[i]==t.data[j]) {i++;j++;  			//i,j各增1}else j=next[j]; 		//i不变,j后退}if (j>=t.length)return(i-t.length);  	//返回匹配模式串的首字符下标else  return(-1);        		//返回不匹配标志
}
/*
int main()
{SqString s,t;StrAssign(s,"ababcabcacbab");StrAssign(t,"abcac");printf("s:");DispStr(s);printf("t:");DispStr(t);printf("位置:%d\n",KMPIndex(s,t));return 1;
}
*/int main()
{SqString s,t;StrAssign(t,"aaba");//StrAssign(t,"aaaac");printf("t:");DispStr(t);int next[100];GetNext(t,next);return 1;
}

力扣例题1408

帮助大家巩固知识点。

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固自己的知识点,和一个学习的总结,对文章有任何问题的还请指出,接受大家的批评,让我改进,如果大家有所收获的话还请不要吝啬你们的点赞和收藏,这可以激励我写出更加优秀的文章。

如果大家还是不太理解的话可以评论区提问,我都会解答,或者可以看b站的【最浅显易懂的 KMP 算法讲解-哔哩哔哩】 https://b23.tv/cvI5JKf

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

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

相关文章

(JAVA)-(网络编程)-初始网络编程

网络编程就是在通信协议下&#xff0c;不同的计算机上运行的程序&#xff0c;进行的数据传输。 讲的通俗一点&#xff0c;就是以前我们写的代码是单机版的&#xff0c;网络编程就是联机版的。 应用场景&#xff1a;即时通信&#xff0c;网游对战&#xff0c;金融证券&#xf…

arm day7

开关中断控制 main.c #include "key_it.h" #include "uart.h" void delay(int ms) {int i,j;for(i0;i<ms;i){for(j0;j<2000;j);} }int main() {key2_it_config();key1_it_config();key3_it_config();all_led_init();char buf[128];gets(buf); whil…

Android APK未签名提醒

最近新建了一个项目&#xff0c;在build.gradle中配置好了签名&#xff0c;在执行打包的时候打出的包显示已签名&#xff0c;但是在上传市场的时候提示未签名。于是排查了好久&#xff0c;发现在build.gradle中配置的minsdk 24&#xff0c;会导致不使用V1签名&#xff0c;于是我…

Kubernetes技术与架构-集群管理

Kubernetes技术与架构提供支撑工具支持集群的规划、安装、创建以及管理。 数字证书 用户可以使用easyrsa、openssl、cfssl工具生成数字证书&#xff0c;在kubernetes集群的api server中部署数字证书用于访问鉴权 资源管理 如上所示&#xff0c;定义一个服务类service用于负…

iptables防火墙(二)

目录 1、SNAT策略及应用 1.1、SNAT 策略概述 1.2、SNAT 策略的应用 2、DNAT 策略及应用 2.1、DNAT 策略概述 2.2、DNAT 策略的应用 3、规则的导出、导入 3.1、规则的备份及还原 3.2、使用 iptables 服务 4、使用防火墙脚本 4.1、防火墙脚本的构成 &#xf…

Python新姿势:用魔法方法玩转对象

文章目录 前言1\. 对象构建2\. 对象属性访问3\. 对象比较4\. 对象输出5\. 对象运算6\. 总结Python技术资源分享1、Python所有方向的学习路线2、学习软件3、入门学习视频4、实战案例5、清华编程大佬出品《漫画看学Python》6、Python副业兼职与全职路线 前言 Python中魔法方法&a…

百兆集成RJ45连接器电路设计原理

华强盛电子导读&#xff1a;HR911105A-H1159A01A-GY百兆网口带变压器原理 百兆集成RJ45连接器电路设计原理涉及到网络通信和电子工程领域。RJ45连接器是一种常见的网络连接器&#xff0c;广泛应用于以太网和其他网络通信中。 以下是百兆集成RJ45连接器电路设计的基本原理&…

冠赢互娱基于 OpenKrusieGame 实现游戏云原生架构升级

作者&#xff1a;力铭 关于冠赢互娱 冠赢互娱是一家集手游、网游、VR 游戏等研发、发行于一体的游戏公司&#xff0c;旗下官方正版授权的传奇类手游——《仙境传奇》系列深受广大玩家们的喜爱。基于多年 MMORPG 类型游戏的自研与运营经验&#xff0c;冠赢互娱正式推出了 2D M…

【数据结构】快速排序(4种方式实现)

前言&#xff1a;前面我们学习了几种相对比较简单的排序&#xff0c;今天我们要一起学习的是快速排序&#xff0c;我们将通过四种方式来模拟实现快排。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:数据结构 &#x1f448; &#x1f4a…

OpenCV(Python)基础—9小时入门版

OpenCV(Python)基础—9小时入门版 # # Author : Mikigo # Time : 2021/12/1 # 一、一句话简介 OpenCV (Open Source Computer Vision Library) 是用 C 语言编写&#xff0c;提供 Python、Java 等语言 API的一个开源计算机视觉库。 二、安装 1、Debian 系使用 apt 安装 O…

Innosetup 调用c# dll 和 c# dll的函数导出

目标需求&#xff0c;基于现在安装包脚本。需要在用户安装和卸载成功时。进行数据记录,所以需要调用c#dll 主要涉及到的知识点 需要理解脚本的文件使用机制脚本的文件dll加载&#xff0c;和dll的调用c# dll的制作&#xff0c;和工具的使用 下面具体介绍 脚本的文件dll加载&…

OSPF的DR与BDR-新版(16)

目录 整体拓扑 操作步骤 1.基本配置 1.1 配置R1的IP 1.2 配置R2的IP 1.3 配置R3的IP 1.4 配置R4的IP 1.5 检测R1与R4连通性 1.6 检测R1与R2连通性 1.7 检测R1与R3连通性 2.搭建基本的OSPF网络 2.1 配置R1 OSPF 2.2 配置R2 OSPF 2.3 配置R3 OSPF 2.4 配置R4 OSPF…

八皇后问题(C语言)

了解题意 在一个8x8的棋盘上放置8个皇后&#xff0c;使得任何两个皇后都不能处于同一行、同一列或同一斜线上。问有多少种方法可以放置这8个皇后&#xff1f; 解决这个问题的目标是找到所有符合要求的皇后摆放方式&#xff0c;通常使用回溯算法来求解。回溯算法会尝试所有可能…

数据结构之树 --- 二叉树 < 堆 >

目录 1. 树是什么&#xff1f; 1.1 树的表示 2. 二叉树 2.1 二叉树的概念 2.2 特殊的二叉树 2.3 二叉树的性质 2.4 二叉树的存储结构 2.4.1 顺序存储 2.4.2 链式存储 3. 二叉树顺序结构的实现 <堆> 3.1 二叉树的顺序结构 ​编辑 3.2 堆的概念及结构 ​编辑…

Appium+python自动化(八)- 初识琵琶女Appium(千呼万唤始出来,犹抱琵琶半遮面)- 下(超详解)

简介 通过上一篇宏哥给各位小伙伴们的引荐&#xff0c;大家移动对这位美女有了深刻的认识&#xff0c;而且她那高超的技艺和婀娜的身姿久久地浮现在你的脑海里&#xff0c;是不是这样呢&#xff1f;&#xff1f;&#xff1f;不要害羞直接告诉宏哥&#xff1a;是&#xff0c;就对…

C单片机数据类型与格式化

C语言数据类型 关键字位数表示范围stdint关键字ST关键字举例unsigned char80 ~ 255uint8_tu8u8 data 128char8-128 ~ 127int8_ts8s8 temperature 25unsigned short160 ~ 65535uint16_tu16u16 counter 5000short16-32768 ~ 32767int16_ts16s16 position 32767unsigned int3…

基于YOLOv5+Deepsort 的PCB缺陷检测及计数系统

背景&#xff1a; PCB&#xff08;Printed Circuit Board&#xff0c;印刷电路板&#xff09;是电子产品中至关重要的组成部分&#xff0c;它承载着电子元器件并提供电气连接。在PCB制造过程中&#xff0c;由于工艺、材料或设备等因素的影响&#xff0c;可能会引入各种缺陷&am…

电表通讯协议DLT645-2007编程

1、协议 电表有个电力行业推荐标准《DLT645-2007多功能电能表通信协议》&#xff0c;电表都支持&#xff0c;通过该协议读取数据&#xff0c;不同的电表不需要考虑编码格式、数据地址、高低位转换等复杂情况&#xff0c;统一采集。 不方便的地方在于这个协议定义得有点小复杂…

Strateg策略模式(组件协作)

策略模式&#xff08;组件协作&#xff09; 链接&#xff1a;策略模式实例代码 注解 目的 正常情况下&#xff0c;一个类/对象中会包含其所有可能会使用的内外方法&#xff0c;但是一般情况下&#xff0c;这些常使用的类都是由不同的父类继承、组合得来的&#xff0c;来实现…

跨境电商迎来综合竞争力比拼时代 五大趋势解读跨境2024

过去几年&#xff0c;跨境电商成为外贸出口增长的一大亮点&#xff0c;随着年底国务院办公厅《关于加快内外贸一体化发展的若干措施》的发布&#xff0c;跨境电商在促进经济发展、助力内外贸一体化发展方面的价值更加凸显。 这是跨境电商变化最快的时代&#xff0c;也是跨境电…