第四十六天学习记录:C语言进阶:KMP算法个人学习方法

news2024/9/22 9:55:53

学习了strstr库函数后,老师让了解KMP算法,这也算是我接触到的第一个算法。
由于这一块得自己翻资料自学,因此初识比较吃力。
后面根据自己的理解方式,个人认为理解KMP算法最关键点就是理解next数组是怎么生成的。
下面说说我理解next的方法,有些简单粗暴。
首先,根据教程上的范例通过改造,直接将生成的next数组的所有元素打印出来。
然后再输入各种字符串,通过“实践”的方式通过观察规律来理解。

我们先不看代码,直接看运行结果:

我们用“ABCDABCE”来举例。
当字符串为“ABCDABCE”时,next数组是{-1,0,0,0,0,1,2,3,0}

下面用图示来理解。
在这里插入图片描述

如上图所示,我们先找到next上第一个正数。

在这里插入图片描述
然后移动到后面的正数,以此类推。
在这里插入图片描述

通过上面三幅图,有没有发现什么规律?

我们可以通过next数组元素中的数字n,观察到在字符串中第一个字符开始的n个字符,和这个数组元素对应位置的字符串向前n个数字是一样的。

我们再举一个更加极端一点的字符串:“AAAAAAAA”

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
我们可以看到,如果按照前面的方式来画这种情况,似乎这里所有的数字都少了1个。这里是为什么呢?个人理解应该是前缀和后缀不能完全重叠。

我们再举个例子:“ABBBBBBB”

在这里插入图片描述
在这里插入图片描述

看到这里,我们可以理解为什么第二串字符串为什么不能根据第一串的方式去圈图。因为要是能圈上整个字符串,那无论什么字符串都能被圈上,这显然是不对的。

因此到这里,我们能够初步理解next数组的生成原理了。知道了原理,就能够更好的理解代码的实现方式了。生成next数组的代码如下:

#include <stdio.h>

void getNext(char* p, int* next) {
	int len = strlen(p);
	int i = 0, j = -1;
	next[0] = -1;
	while (i < len) {
		if (j == -1 || p[i] == p[j]) {
			++i;
			++j;
			next[i] = j;
		}
		else {
			j = next[j];
		}
	}
}

int main() {
	char p[] = "ABCDABCE";
	int next[9];
	int i = 0;
	getNext(p, next);
	for (i = 0; i < 9; i++)
	{
		printf("%d ", next[i]);//直接将next所有元素打印出来
	}
	printf("\n");
	return 0;
}

KMP算法:

以下前缀加了ChatAI答的都是笔者问询ChatAI给出的答案,毕竟作为初学者的笔者还无法写出官方理论性强的文字,囧。ChatAI的回答可以供大家参考。
ChatAI答:
KMP算法是一种用于字符串匹配的高效算法,其核心思想是利用已知的部分匹配信息来减少匹配的次数。

具体来说,KMP算法通过预处理模式字符串,得到一个部分匹配表(Partial Match Table或称为Next数组),该表记录了在每个位置时,失配后应该跳到哪个位置继续匹配,从而减少了匹配的时间复杂度。

KMP算法的匹配过程如下:

  1. 对模式串p进行预处理,得到部分匹配表next数组;

  2. 从文本串s的第一个字符开始和模式串p进行匹配,假设当前已匹配的长度为i,模式串的指针为j;

  3. 如果s[i]和p[j]匹配,则令i++和j++,继续匹配下一个字符;

  4. 如果s[i]和p[j]不匹配,根据next数组,将模式串指针j跳到next[j]的位置继续匹配(即,将模式串向右移动j-next[j]个位置),直到模式串匹配到末尾,或者匹配成功。

KMP算法的时间复杂度是O(n),其中n是文本串的长度。预处理部分匹配表的时间复杂度是O(m),其中m是模式串的长度。

KMP算法在实际应用中,由于其高效性和简单实现,被广泛用于字符串匹配领域,例如文本编辑器、代码编辑器等等。

问:可以举个KMP算法的例子吗?用C语言
ChatAI答:
下面是一个使用C语言实现的KMP算法的例子,用于在文本串中查找模式串的位置。

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <string.h>

void getNext(char* p, int* next) {
	int len = strlen(p);
	int i = 0, j = -1;
	next[0] = -1;
	while (i < len) {
		if (j == -1 || p[i] == p[j]) {
			++i;
			++j;
			next[i] = j;
		}
		else {
			j = next[j];
		}
	}
}

int kmp(char* s, char* p, int* next) {
	int slen = strlen(s);
	int plen = strlen(p);
	int i = 0, j = 0;
	while (i < slen && j < plen) {
		if (j == -1 || s[i] == p[j]) {
			++i;
			++j;
		}
		else {
			j = next[j];
		}
	}
	if (j == plen) {
		return i - j;
	}
	else {
		return -1;
	}
}

int main() {
	char s[] = "ABCDABCABCDABCDABCE";
	char p[] = "ABCDABCE";
	int next[10];
	getNext(p, next);
	int pos = kmp(s, p, next);
	if (pos != -1) {
		printf("在位置%d匹配上了\n", pos);
	}
	else {
		printf("没有匹配\n");
	}
	return 0;
}

在这个例子中,我们用kmp函数实现了KMP算法的主要逻辑。首先对模式串进行预处理,得到部分匹配表next数组。然后,使用i和j两个指针分别遍历文本串和模式串,进行匹配。如果匹配成功,则i和j都向后移动一位,否则根据next数组更新模式串的指针j。最后,如果匹配成功则返回模式串在文本串中的起始位置,否则返回-1表示模式串不在文本串中。

在本例中,s和p分别代表文本串和模式串,其输出结果应该是"在位置11匹配上了",表示模式串在文本串中的起始位置是第11个字符。

如果因为ChatAI回答得字数太多看得想犯困,那么我们还是通过简单图示来分析吧,就用上面的例子:

程序开始前,初始化之后如下图所示:
在这里插入图片描述
在这里插入图片描述

显然,在前面7个字符,s[i],p[j]都是一样的,都执行了绿色矩形框里的代码。但是,当i=7,j=7的时候,因为s[7]!=s[7],很遗憾,只能执行红色矩形框里的代码了。(j说:明明差一点点就能成功了……)
j=next[7],next[7]的值是3,因此j从7变成了3.
在这里插入图片描述
从这里就可以看出KMP和strstr的区别。如果是库函数strstr,此时i会回退到1,j也会回退到0重新开始匹配,而在这个例子的KMP算法中则省去了很多不必要的步骤。
当i=7,j=3时,s[7]!=p[3]还是执行的红色矩形框中的代码,因此j=next[3],j从3变成0
在这里插入图片描述
我发现,s[7]==p[0],因此执行绿色矩形框的代码,i和j同时+1

在这里插入图片描述
又是似曾相识的感觉,当i=14,j=7时,因为s[14]!=p[7],再次执行红色矩形框里的代码,j从7变成3.
在这里插入图片描述
但是这一次,神奇的事情发生了!
s[14]==p[3],他们都是’D’!
这一次j没有变成0,而是作为3重新执行绿色代码!
在这里插入图片描述
这一次,当j再一次到7的时候,终于没有被打回3,而是执行绿色矩形框代码顺利的变成了8,至此跳出循环。
在这里插入图片描述
因为跳出循环后,j等于8,因此返回了18-7=11。
而实际上,s确实在第11的位置开始,包含了整p.

通过感知这个例子,明显能够感觉到KMP算法在时间复杂度上比strstr简单了很多。

其他咨询ChatAI的问题:
问:next[]中的数字代表什么意思?

ChatAI答:
在KMP算法中,next[]数组是用来保存“最长的相等的前缀后缀长度”的数组。举个例子,如果模式串的某一位置i之前的字符串为"abab",那么next[i]就表示“abab”的最长相等前缀后缀的长度。也就是说,next[i]存储的是模式串中,第i个字符之前的子串中最长公共前后缀的长度。

利用next[]数组,KMP算法能够避免在匹配过程中对已经匹配过的部分进行重复匹配,从而提高算法的效率。当匹配失败时,我们可以通过next数组中存储的最长公共前后缀的长度,将模式串向右移动对应的位数,从而继续进行匹配。这就是KMP算法的关键思想。

因此,next[]数组实际上是KMP算法中非常重要的一部分,通过保存最长相等的前缀后缀长度,能够使得算法实现方便而有效地完成字符串匹配。

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

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

相关文章

对于西瓜书神经网络的c#手写版本

本文根据西瓜书第五章中给出的公式编写&#xff0c;书中给出了全连接神经网络的实现逻辑&#xff0c;本文在此基础上编写了Mnist10手写10个数字的案例&#xff0c;网上也有一些其他手写的例子参考。demo使用unity进行编写&#xff0c;方便且易于查错。 该案例仅作为学习&#x…

CSS零基础快速入门(详细教程)

1&#xff0c;CSS概述 CSS是层叠样式表&#xff0c;由Cascading Style Sheets简称而来。 CSS的功能为&#xff1a;能够对网页中元素位置的排版进行像素级精确控制&#xff0c;实现美化页面的效果&#xff0c;并且能够做到页面的样式和结构分离。 CSS的作用效果跟我们日常使用…

这个 快速排序详解过程 我能吹一辈子!

文章目录 快速排序概念快速排序递归实现Hoare版本挖坑法前后指针法 快速排序非递归实现Hoare版本挖坑法前后指针法 快速排序的俩个优化三数取中小区间优化 快速排序概念 快速排序是公认的排序之王&#xff0c;快速排序是Hoare于1962年提出的一种二叉树结构的交换排序算法&#…

【Linux】静态库与动态库

前言 对于C/C的学习者&#xff0c;我们经常听到C/C的标准库&#xff0c;我们也经常使用它们&#xff0c;但是我们在使用的时候经常只包含一下头文件&#xff0c;然后就使用了&#xff0c;我们从来没有认真的研究过C/C的标准库&#xff0c;而且C/C的头文件中只有声明并没有声明的…

文件上传,解析漏洞编译器安全(23)文件上传为什么加空格和修改为其他符号(例如换行符)问题

apache低版本解析漏洞 这个网站目录里有两个文件&#xff0c;一个是正常的php文件&#xff0c;另一个xx.php.xxx&#xff0c;源码是php源码&#xff0c;命名的文件&#xff0c;而访问中xxx的文件依旧可以执行出php代码的结果&#xff0c;而xxx就能当php文件解析&#xff0c;这…

uvc摄像头驱动uvc设备的注册分析

uvc摄像头驱动uvc设备的注册分析 文章目录 uvc摄像头驱动uvc设备的注册分析uvc_inituvc_probeuvc_register_videouvc_register_chainsuvc_register_termsuvc_register_video uvc_ioctl_opsuvc_fops uvc_init /driver/media/usb/uvc/uvc_driver.c /** UVC 驱动结构体*/ struct…

每日学术速递5.19

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.On the Hidden Mystery of OCR in Large Multimodal Models 标题&#xff1a;论大型多模态模型中 OCR 的隐藏奥秘 作者&#xff1a;Yuliang Liu, Zhang Li, Hongliang Li, Wenwen…

计算机图形学-GAMES101-11

显式几何的表示方法 Point Cloud 使用一系列点表示物体的表面。理论上可以表达空间中任何物体。如物体扫描会得到空间中一系列点&#xff0c;但在建模时我们要提取出大量多边形的面。点云对密度要求很高&#xff0c;因此不常使用。 Polygon Mesh 使用三角形表示物体。涉及三…

备份树莓派SD卡 — 保姆级教学

在我们树莓派项目开发的过程中&#xff0c;经常遇到以下问题&#xff1a; 1.自己辛辛苦苦开发出来的项目&#xff0c;害怕内存卡损坏&#xff0c;系统被破坏掉&#xff0c;想做一个备份。 2.自己的树莓派内存卡内存不够了&#xff0c;想将原来卡上的内容放在新的大容量内存卡…

Arm微架构分析系列3——Arm的X计划

1. 引言 前文介绍了Arm公司近几年在移动处理器市场推出的Cortex-A系列处理器。Cortex-A系列处理器每年迭代&#xff0c;性能和能效不断提升&#xff0c;是一款非常成功的产品。但是&#xff0c;Arm并不满足于Cortex-A系列每年的架构小幅度升级&#xff0c;又推出了X计划&#x…

esp32CAM环境安装教程---串口驱动安装

前言 &#xff08;1&#xff09;本人安装好arduino 的ESP32环境之后&#xff0c; 发现一直下载不进去程序。一直说Cannot configure port, something went wrong. Original message: PermissionError。 &#xff08;2&#xff09;查阅了很多资料&#xff0c;用了各种办法&#…

怎么消除文法的左递归性

除文法的左递归性可以采用以下方法&#xff1a; 直接左递归转换为间接左递归消除间接左递归 举例说明&#xff1a; 直接左递归转换为间接左递归 原文法&#xff1a;A → Aα | β 转换后的文法&#xff1a;A → βA A → αA | ε 例如&#xff1a;S → Sabc | ε 转换后…

4. QT中的事件函数 --- 鼠标事件、键盘事件、定时器事件、绘图事件

1. 说明 在QT的控件或者窗口当中&#xff0c;如果对于当前鼠标或者键盘的功能需要自己定义&#xff0c;可以重写父类当中对应虚函数&#xff0c;主要包括以下几个&#xff1a; //键盘按键按下 virtual void keyPressEvent(QKeyEvent *event); //键盘按键抬起 virtual void ke…

11.1网络编程

多线程 一、基础知识概念相关API二、任务创建一个简单的本地客户端迭代服务器select系统调用并发服务器数据报三、总结四、问题一、基础知识 概念 网络编程中客户端和服务器指的是进程,而不是常提到的机器或者主机。注意三个概念:请求、响应、事务。 网络编程中客户端-服务器…

面向对象的三大特性之继承(C++)

文章目录 继承的概念和定义概念定义定义格式继承关系和访问限定符继承基类成员访问方式的变化 基类和派生类对象赋值转换继承中的作用域派生类的默认成员函数继承与友元继承与静态成员菱形继承与虚拟继承菱形继承虚拟继承 继承的总结与反思继承和组合 继承的概念和定义 概念 继…

微信小程序 nodejs+vue+uniapp付费自习室图书馆教室座位系统-

系统分为用户和管理员角色 管理员的主要功能有&#xff1a; 1.管理员输入账户登陆后台 2.个人中心&#xff1a;管理员修改密码和账户信息 3.用户管理&#xff1a;对注册的用户信息进行添加&#xff0c;删除&#xff0c;修改&#xff0c;查询 4.自习室管理&#xff1a;对系统的自…

由浅入深Netty协议设计与解析

目录 1 为什么需要协议&#xff1f;2 redis 协议举例3 http 协议举例4 自定义协议要素4.1 编解码器4.2 什么时候可以加 Sharable 1 为什么需要协议&#xff1f; TCP/IP 中消息传输基于流的方式&#xff0c;没有边界。 协议的目的就是划定消息的边界&#xff0c;制定通信双方要…

每日学术速递5.18

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Make-A-Protagonist: Generic Video Editing with An Ensemble of Experts 标题&#xff1a;Make-A-Protagonist&#xff1a;与专家合奏的通用视频编辑 作者&#xff1a;Yuyang Z…

云端一体助力体验升级和业务创新

随着音视频和AI技术的发展&#xff0c;在满足用户基础体验和需求情况下&#xff0c;更极致的用户体验和更丰富的互动玩法&#xff0c;成为各个平台打造核心竞争力的关键。LiveVideoStackCon 2022 北京站邀请到火山引擎视频云华南区业务负责人——张培垒&#xff0c;基于节跳动音…

虚幻引擎4利用粒子系统实现物体轨迹描绘

虚幻引擎4利用粒子系统实现物体轨迹描绘 目录 虚幻引擎4利用粒子系统实现物体轨迹描绘前言粒子系统利用粒子系统实现物体轨迹描绘创建粒子系统将粒子系统的产生位置绑定到运动物体上 小结 前言 由于在物体运动时&#xff0c;想要观察其总的运动轨迹&#xff0c;以便对其控制做…