【KMP算法】

news2024/10/5 10:59:02

KMP算法核心剖析:

关于KMP算法,建议先了解 BF算法

KMP算法是用来解决字符串匹配问题的高级算法,看完这篇文章,你应该能理解KMP算法。

KMP算法和BF算法唯一的区别在于:主串的i 并不会回退,子串的j也不会回退到0位置。

KMP算法的核心在于求出子串的next数组

所谓的next数组,其实存放的就是子串回退的位置的下标。

下面来演示如何求出一个子串的next 数组
假设有一个字符串:“ababcabcdab”, 求该字符串的next数组:

规定:next[0] = -1 , next[1] = 0

下面就来求next[2] ,next[3]…

在这里插入图片描述
如上图:
那么next[2]如何求呢?

来看下面:
规则:

1、规则:找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标0字符开始,另一个以j - 1下标字符结尾。
2、不管什么数据next[0] =-1;next[1]= 0;在这里,我们以下标来开始,而说到的第几个第几个是从1开始;

第二点已经说过,那么第一点是什么意思呢?
看下面:
对于p数组:该数组存有上面举例的字符串: ababcabcdab ,
p[2] = a, 求这个位置的next 值,根据规则1,找到匹配成功部分的两个相等的真子串,一个下标0 字符开始, 另一个是j-1 结尾

意思就是一个串从下标0开始,另一个串从下标1结尾,对于p[2] , p[0] = a, p[2-1] = b,没有两个相等的真子串,即长度为0 ,故next[2] = 0。

看不懂没关系,继续看下面:
求next[3]:,p[0] = a,p[3-1] = a,所以该子串是以a开始,另一个串以a结尾,所以只有a 和 a 这两个串匹配,长度为1 ,故next[3] = 1;
在这里插入图片描述

求next[4]:,p[0]= a,p[4-1] = b,该两串以a开始,以b结尾,故有两个串是ab和ab匹配,长度为2,所以next[4] = 2;

在这里插入图片描述

求next[5]:,p[0] = a,p[5-1] = c,该两串一个以a开始,一个以c结尾,找不出相等的两子串,故长度为0,所以next[5] = 0.

求next[6]:,p[0]= a,p[6-1] = a,该两串以a开始,另一个以a结尾,然而串1往后走,走到以a结尾是 aba, 串2往回走,走到以a开始是
abca ,两者不同,故长度只有a 和 a 两个串,长度为1,即next[6] = 1

在这里插入图片描述

求next[7]:,p[0] = a,p[7-1] = b,该串以a开始,以b结尾,有这样的长度的,只有串1 为ab,串2 为ab ,如下图:
在这里插入图片描述

求next[8],p[0] = a,p[8-1] = c ,由于c是新增的,故长度为0,没有相同的两串是以a开始,另一个以c结尾。next[8] = 0

求next[9]: p[0] = a , p[9-1] = d ,由于d也是新增的,没有相同的两串是一个以a开始,一个以d结尾,故长度为0 .
求next[10] ,p[0] = a,p[10-1] = a,一串以a开始,一串以a结尾,故只有这两个串a和a,故长度为1. next[10] = 1.
在这里插入图片描述
求完next数组后, KMP算法核心就结束了。

有一条规律:next[i]往后走的时候,后一个数如果比前一个数大,那一定是大一位 , 如果比前一个数小,可能小1,2,…
重点是后一个数如果比前一个数大,那一定是大一位
由此我们可以得到另一条结论:
在这里插入图片描述

先看上图:假设i 和 k 的位置如上图:(k是如果在i的位置与主串不匹配,那么子串回退到的位置:就是k下标的位置。)
我们求next数组的时候,是肉眼来求的,但是当我们写代码的时候,next数组是一个个往后求的,所以这里,就是已知 i ,求 i+1的next数组,
情况1>>如果p[i] == p[k],那么next[i+1] = k+1(注意:i和k都是下标)
所以next[i+1] = 1+1 = 2。

情况2>> 如果p[i] !=p[k] , 那么k就不是我们要找的, 所以k需要继续回退,k回退到他所在的next[k] 位置,即 k = next[k],next[k] = 0,
所以k继续回退到0位置,此时 p[i] == p[k] ,所以next[i+1] = k+1 = 0+1 = 1.

情况2其实就是继续回退,找到情况1。

上面这两条结论,就是我们要写代码时 要用到的结论,
但是我们正常来求next数组,是用肉眼来求的,不需要这些结论。

总结:

写代码求next数组时用到的两条结论:

情况1>>如果p[i] == p[k],那么next[i+1] = k+1(注意:i和k都是下标)
所以next[i+1] = 1+1 = 2。

情况2>> 如果p[i] !=p[k] , 那么k就不是我们要找的, 所以k需要继续回退,k回退到他所在的next[k] 位置,即 k = next[k]

下面就可以开始写代码了,

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

void GetNext(const char* sub, int*next,int len_sub)
{
	assert(sub);
	next[0] = -1;
	next[1] = 0;
	int j = 2;//j是下标->下标从0开始
	int k = 0;//k是下标
	while (j < len_sub)//所以这里没有等号
	{
		if (k==-1 || sub[j-1] == sub[k]) // 情况1
		{ // k==-1也就是极端情况,一开始匹配就不相等,既然不相等,长度就为0,next对应的长度就为0
			next[j] = k + 1;
			j++;
			k++;
		}
		else //情况2
		{
			k = next[k];
		}
	}
}

先来趁热打铁,求next数组。
在这里插入图片描述

看不懂代码先不急,看看上图,
我们求的是j 所对应的next数组的值,现在知道了 j-1 了,故从j-1开始比较,(也因为j 是下标,下标从0开始 )

会求next数组,也就成功了%60

int KMP(const char* str, const char* sub , int pos)
{
	assert(str && sub);
	int len_str = strlen(str);
	int len_sub = strlen(sub);
	if (len_str == 0)
	{
		return -1;//主串长度为0,找不到
	}
	if (len_sub == 0)
	{
		return 0;//子串长度为0,返回主串起始位置
	}
	if (pos<0 || pos>=len_sub)
	{
		return -1;//参数不在主串长度的有效范围内
	}
	int* next = (int*)malloc(sizeof(int) * len_sub);
	GetNext(sub,next,len_sub);
	//求子串的next数组

	int i = pos;//记录主串位置
	int j = 0;//记录子串位置

	while (i < len_str && j< len_sub)
	{
		if (j==-1 || str[i] == sub[j])
		{//j==-1是极端情况,即一开始匹配就不相等,
			i++;
			j++;
		}
		else
		{
			j = next[j];//j回退到next[j]的位置
		}
	}
	if (j >= len_sub)
	{
		return i - j;//子串找完了,找到了
	}
	return -1;//退出循环只有两者情况,不是上面那种,就是找完主串都找不到
}


int main()
{
	printf("%d\n", KMP("abbbcdef", "bbc", 0));
	return 0;
}

这是剩下的%40,情况与BF算法类似,你可以尝试着运行,看看结果正确与否。

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

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

相关文章

java: “abstract 抽象类” 与 “ interface 接口” 的妙用之道

java: “abstract 抽象类” 与 “ interface 接口” 的妙用之道 每博一文案 有句很扎心的话&#xff0c;我对这世间唯一的不满就是这世间总是让更懂事的人承受的更多。 生活中&#xff0c;往往你越善解人意&#xff0c;就越没人在乎你的委屈&#xff0c;时间&#xff0c;让你学…

Linux下使用nginx搭建文件服务器

搭建后访问效果图 安装nginx 1、安装依赖 yum install -y gcc pcre-devel zlib-devel openssl openssl-devel 2、下载nginx mkdir -p /www/nginx cd /www/nginx wget http://nginx.org/download/nginx-1.21.0.tar.gz tar -xvf nginx-1.21.0.tar.gz 3、安装nginx cd nginx…

redis远程操作常见问题

Connection error: Connection refused 出现该错误的原因是未开启远程连接&#xff0c;将本地ip注释掉&#xff1a; Connection error: The remote host closed the connection 出现该错误的原因是&#xff0c;需要关闭安全模式&#xff0c;才可运行其他ip访问&#xff1a; 当…

websocket简单实现

websocket简单实现 websocket是HTML5下一种新的协议&#xff0c;本质上websocket是一个基于tcp的协议。它实现了浏览器与服务器之间的双向通信&#xff0c;能更好的节省服务器资源和宽带并实现实时的通信。 websocket的几个优点? 1、使用的资源少&#xff0c;因为它的头更小。…

Footprint Analytics 如何帮助区块链研究人员进行数据研究

管理一个人的数字资产是区块链技术提供的主要应用&#xff0c;但管理的另一面是责任。 就像区块链让任何人都能完全保管他们的加密货币一样&#xff0c;如果你被骗、被黑或被诈骗&#xff0c;几乎没有追索权。链上研究是预防为主&#xff0c;解决问题的方法并不存在。 例如&a…

个推TechDay治数训练营直播回顾 | 企业级标签体系建设实践

标签作为当下最普遍的数据资产类型之一&#xff0c;对企业洞察用户画像、开展精细化运营等具有重要的支撑作用。企业标签体系的建设并非一蹴而就的&#xff0c;需要结合业务视角进行整体的规划&#xff0c;更涉及到复杂的数据治理和数据资产管理等工作。 本文对个推TechDay“治…

复习 [kuangbin带你飞]专题5 并查集

目录1. poj 2236 Wireless Network2. poj 1611 The Suspects3. hdu 1213 How Many Tables4. hdu 3038 How Many Answers Are Wrong5. poj 1182 食物链6. poj 1417 True Liars7. poj 1456 Supermarket8. poj 1733 Parity game9. poj1984 Navigation Nightmare10. poj 2912 A Bug…

MYSQL IN EXISTS LEFT JOIN 结果不同的问题?

随着问问题的同学越来越多&#xff0c;公众号内部私信回答问题已经很困难了&#xff0c;所以建立了一个群&#xff0c;关于各种数据库的问题都可以&#xff0c;目前主要是 POSTGRESQL, MYSQL ,MONGODB ,POLARDB ,REDIS 等&#xff0c;期待你的加入&#xff0c;加群请添加微信li…

智慧园区建设面临挑战,该如何应对?

随着全球物联网、移动互联网、云计算等新一轮信息技术的迅速发展和深入应用&#xff0c;“智慧园区”建设已成为发展趋势。近年来&#xff0c;我国的产业园区也向着智慧化、创新化、科技化转变。中国经济正在进入转型升级的关键时期&#xff0c;各地产业竞争态势越发激烈。可以…

ClickHouse 大数据量的迁移方式

关于Clickhouse 备份方式&#xff0c;其官方网站上就提供了多种备份方式可以参考&#xff0c;不同的业务需求有不同的使用场景&#xff0c;需要使用不同的备份方式&#xff0c;不存在一个通用的解决方案可以应对各种情况下的ClickHouse备份和恢复。今天这个文字&#xff0c;我们…

Qt+第三方库开发遇到的坑---kalrry

Qt依赖UG库开发遇到的坑---kalrry一、依赖引入坑二、Qt在Debug时报错1、编译器是 **MSVC** 还是 **MinGW**2、编译器 32位 还是 64位三、QtMSVC编译后中文乱码四、不能将const char*类型的值分配到const* 类型的实体五、debug编译后再发布程序无法运行六、Qt 环境配置提示警告警…

Spring @Transactional注解事务六大失效场景

Transactional事务失效场景1&#xff1a;注解在非public修饰的方法上。 原因&#xff1a;Spring强制的要求。 代码示例&#xff1a; Transactionalprivate void createOrder(){} Transactional事务失效场景2&#xff1a;注解在被final关键字修饰的方法上。 原因&#xff1a;Spr…

推荐一款好用的设备维护管理系统,你用过了吗

设备维护管理系统层出不穷&#xff0c;找到一款好用的适配的&#xff0c;也要花费大量的时间去挑选&#xff01; 对于企业来说&#xff0c;一个好的设备管理系统应该能够&#xff1a; 1. 适应企业高度场景化的设备管理工作&#xff0c;覆盖设备的采购、常规检查、养护、添装、…

“三刷”牛客网844道Java题目,易错知识点总结,带你清扫Java基础面试障碍

目录 前言 1、子类通过哪些办法&#xff0c;可以调用继承自父类的方法&#xff1f; 2、volatile、Lock、transient 哪个关键字不能用来处理线程安全 3、Hashtable 和 HashMap 的区别是&#xff1f;&#xff08;容易忽略的两点&#xff09; 4、如何声明了一个适合于创建50个字…

flask框架实现文件下载接口

方式一&#xff1a; app.route("/download1") def download():# return send_file(test.exe, as_attachmentTrue)return send_file(2.jpg)# return send_file(1.mp3)如果不加as_attachment参数&#xff0c;则会向浏览器发送文件&#xff0c;比如发送一张图片&#x…

Revit技巧:快速隔离一个小构件,拉伸屋顶转折处连接

一、Revit中如何快速单独隔离一个小构件 今天跟大家分享的是一个快速隔离的小技巧&#xff0c;你可以理解为快速用剖面框拉成你需要的构件区域。我就举个例子让大家简单容易理解&#xff0c;如图1所示&#xff0c;假设我只需要编辑那个墙的装饰&#xff0c;但又需要用剖面框&am…

704二分查找法--搜索区间

二分查找法–搜索区间的深入理解 二分查找法是算法学习中很基础的算法&#xff0c;但是其也是很重要的算法&#xff0c;将二分查找法搞明白对后续算法的学习有着事半功倍的作用。 本体难点&#xff1a;二分搜索区间的判断 搜索区间 [left,right] int search(vector<int>…

026_SS_MoFusion A Framework for Denoising-Diffusion-based Motion Synthesis

MoFusion: A Framework for Denoising-Diffusion-based Motion Synthesis 本文提出了一种利用diffusion生成人体motion的方法。这种方法可以将音频和文本作为条件。 损失函数 对于diffusion的损失中&#xff0c;加入了运动损失。 其中第一项 LdaL_{da}Lda​ 是原始的diffusio…

微信公众号如何接入ChatGPT机器人

不难&#xff0c;代码总共也就25行&#xff0c;大致逻辑如下。 总共分为是下面两步 文章目录在云服务器上部署自定义消息处理服务微信公众号配置自己的消息处理服务器在云服务器上部署自定义消息处理服务 这里需要我们自定义来处理用户发送过来的消息 首先导入werobot&#x…

Linux操作系统实验2——进程描述

实验要求&#xff1a; 1.查看task_struct的结构&#xff0c;找到其中的pid&#xff0c;state&#xff0c;prio&#xff0c;parent pid字段 2.在task_struct结构中找到vma相关字段&#xff0c;vm_start,vm_end,vm_next 3.打印指定pid的基本信息&#xff0c;包括基本信息及VMA内存…