KMP算法详解

目录

KMP算法的介绍

详解KMP算法

next数组的填充

代码实现


KMP算法的介绍

KMP算法是解决字符串匹配问题的一个经典算法,字符串匹配问题就是查找子串T是否在主串S中存在,其中在KMP算法中,主串也被称为目标串,子串也被称为模式串。

而在认识KMP算法之前,我们可以先认识BF算法,这是一个简单粗暴的算法:从主串开始逐个对比,成功找到就停止,并进行一系列操作,没有找到就回溯,直到主串走到最后为止。图例如下:

非原图,如有侵权,请联系作者标题

可以说, BF算法的问题是由目标串(主串)决定的,因为在BF算法的过程中,是目标串的下标在不断移动,而模式串只是被动的去和目标串匹配。而在KMP算法中,问题是由模式串决定的,而不是目标串,这么做可以大大提升字符串匹配的效率。例如在上方的例子中,KMP算法是直接跳过了其第二趟、第三趟,直接来到了第四趟,因为前两个字符是相等的,再匹配的意义以及不大了。如下图所示:

非原图,如有侵权,请联系作者标题标题

如果我们假定目标串的长度为m,模式串的长度为n,那么BF算法的时间复杂度是O(mn)的(因为需要不断回溯),但KMP算法的时间复杂度是O(m+n)。至于KMP算法到底是怎么回事呢?还请继续阅读下面的部分。

详解KMP算法

在KMP算法中,一个关键内容就是next数组,next数组的内容是与模式串的内容一一对应的,next数组中存放的是一个个整型元素,这些整型元素的意义代表:如果当前位置模式串的字符与目标串的不匹配,那么模式串的下标就跳转为这个整型元素。以上面“ABCABA”的字符串为例:

至于这个next数组的内容是怎么来的会在下一个内容板块详细讲解,这里先假定存在这个next数组,那么在此条件下KMP的过程如下:

所以这也就算是为什么说KMP算法的的问题是由模式串决定的,KMP中主要是对模式串进行操作,而主串基本上是“不动”的。在已知next数组的条件下执行KMP的代码如下:

int KMPSeek(char* String, char* SubString) 
//字符串匹配,找到了就返回所在主串位置的下标,没找到就返回-1
{
	
	int lenS = strlen(String); //目标串的长度
	int lenT = strlen(SubString); //模式串的长度
	int next[255]; 
	InitNextArray(SubString, next); //填充next数组(这里先不用管)
	int subscript = -1; //返回的下标
	int i = 0, j = 0;
	while (i < lenS)
	{	
		if (j == 0)	//更新下标
			subscript = i;
		//子串与主串字符比较
		if (j == -1 || SubString[j] == String[i])		
			i++, j++; //逗号运算符。		
		else	
			j = next[j];	
		//检测是否完成匹配
		if (j == lenT - 1 && SubString[j] == String[i])
		{
			printf("找到了,下标是:%d\n", subscript); 
			return subscript;
		}
	}
	//走出循环就一定是没找到
	puts("404  Not Found!");
	return -1;
}

next数组的填充

要知道,KMP算法的重点和难点就是next数组。

在学习如何填充next数组之前我们还需要了解 “最长公共前后缀” 这么一个概念,其实这并不是一个多么复杂的概念我们这里简单介绍一下,前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。所以最长公共前后缀就是字面意思了。以“ABCAB“这么一个字符串为例,其前缀有 {{A}, {AB}, {ABCA}} 这么几个,后缀有{{B}, {AB}, {CAB}, {BCAB}} 这么几个,那么最长公共前后缀就是”AB“,其长度为2。

了解了什么是最长公共前后缀之后,我们再来看next数组,next数组的内容存放的就是从下标为0的位置开始直至当前下标的前一个元素的位置(即从下标为0至下标为i-1的字符串)这个字符串的最大公共前后缀的长度。

那么接下来该这么求其最长公共前后缀呢?首先设置两个整型变量i,j(实质就是数组下标,i为前缀下标,j为后缀下标),起初i为-1,j为0,然后分别让其加一,即i为0,j为1。然后开始我们的填充过程,先判断i位置的字符是否相等,相等就使i和j同时后移,然后让此时的next[j]等于i,如果不相等就j不动i赋值为next[i],这个过程很像KMP的过程。然后循环往复,直至j走到最后。

这里总结一下填充next数组的核心思路:

1 - 前缀是固定的,后缀是相对的
2 - 填充next的过程就是“以存在的前后缀最大公共部分为字串,以0到后缀的位置为主串”,不断进行“自身KMP”的过程

具体的过程示例图解如下,以字符串”abxabwabxad“为例 

非原图,如有侵权,请联系作者标题

代码实现

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

void InitNextArray(const char* SubString, int* next) //填充next数组,next数组存放的是失配时跳转的数组下标
{
/*
	填充next数组的核心思路:
	1 - 前缀是固定的,后缀是相对的
	2 - 填充next的过程就是“以存在的前后缀最大公共部分为字串,以0到后缀的位置为主串”,不断进行“自身查找”的过程
*/
	//puts(SubString);
	int lenT = strlen(SubString);
	next[0] = -1; //
	int i = -1, j = 0; //i是前缀指针,j是后缀指针
	while(j < lenT) 
	{
		if (i == -1 || SubString[i] == SubString[j])
		{
            /*next[++j] = ++i; //未优化,不能高效处理重复字符 */ 
            //优化后的做法
			if (SubString[++i] == SubString[++j]) //i,j加一后再次对比,减少跳转次数
				next[j] = next[i];
			else
				next[j] = i;
		}
		else
		{
			i = next[i];
		}
	}
}
int KMPSeek(char* String, char* SubString) //字符串匹配,找到了就返回所在主串位置的下标,没找到就返回-1
{
	
	int lenS = strlen(String); //S串的长度
	int lenT = strlen(SubString);
	int next[255];
	InitNextArray(SubString, next);
	int subscript = -1; //返回的下标
	int i = 0, j = 0;
	while (i < lenS)
	{	
		if (j == 0)	//更新下标
			subscript = i;
		//子串与主串字符比较
		if (j == -1 || SubString[j] == String[i])		
			i++, j++;		
		else	
			j = next[j];	
		//检测是否找到
		if (j == lenT - 1 && SubString[j] == String[i])
		{
			printf("找到了,下标是:%d\n", subscript); 
			return subscript;
		}
	}
	//走出循环就一定是没找到
	puts("404  Not Found!");
	return -1;
}

int main()
{
	char strS[] = "nmiucowabcooxx"; //S串 - 主串
	char strT[] = "abc"; //T串 - 子串
	KMPSeek(strS, strT);
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

图片转pdf无水印版怎么转换?快收藏这三种免费转换方法!

图片转pdf无水印版怎么转换&#xff1f;在日常生活中&#xff0c;为了节省批量图片发送的时间&#xff0c;我们通常会将多张图片转换成PDF文件格式文档&#xff0c;然后发送给他人。 目前在市场上有很多软件可以将图片转PDF。你想知道哪个软件可以将图片转PDF没有水印吗&#…

Java命令行参数

目录 一、引入依赖 二、方法实战 三、方法讲解 本文我们介绍一个命令行工具&#xff0c;Apache Commons CLI。 在我们执行java的jar包时&#xff0c;常用的命令是 java -jar hellowork.jar # 或者 nohup java -jar hellowork.jar >>/data/log.txt2>&1 & …

这6个超好用的免费图片素材网站,赶紧收藏~

6个高质量图片素材网站&#xff0c;免费可商用&#xff0c;记得收藏&#xff01; 1、菜鸟图库 https://www.sucai999.com/pic.html?vNTYxMjky 菜鸟图库是我推荐过很多次的一个设计素材网站&#xff0c;除了设计类&#xff0c;还有很多自媒体可以用到的素材&#xff0c;比如高…

如何免费使用ChatGPT 4?

自从ChatGPT发布以来&#xff0c;它就取得了巨大的成功。无论是常春藤法学考试还是商学院作业&#xff0c;ChatGPT都被用于各种试验。统计数据显示&#xff0c;ChatGPT每月吸引约9600万用户。随着ChatGPT的巨大成功&#xff0c;Open AI最近推出了它的最新版本&#xff0c;名为“…

数据库第一个实验

啦啦啦啦啦&#xff0c;数据库终于要实验了&#xff0c;很担心做不好&#xff0c;要是挂了怎么办 只是自己的作业&#xff0c;可能会有问题&#xff0c;欢迎前来指正 一、题目&#xff08;100分&#xff09; 一、创建后面给出的这6个表&#xff08;20分&#xff09; 二、用不同…

论文阅读_Segment_Anything

论文信息 name_en: Segment Anything name_ch: 切分任何东西 paper_addr: http://arxiv.org/abs/2304.02643 doi: 10.48550/arXiv.2304.02643 date_read: 2023-04-07 date_publish: 2023-04-05 tags: [‘深度学习’,‘多模态’] author: Alexander Kirillov, Meta AI Research…

用in函数嵌入子查询作为条件时查出结果为空

用in函数嵌入子查询作为条件时查出结果为空 问题&#xff1a; SELECT * FROM SGGCDB_VIEW sv WHERE RES_ID IN (SELECT urrv.RES_ID FROM IBPS_ERP.USER_ROLE_RES_VIEW urrv WHERE urrv.ID_ 1069978138403930112 )结果未空值。 原因&#xff1a; 首先&#xff0c;SELECT u…

【Linux系统:进程控制】

目录 1 进程创建 1.1 fork函数 1.2 写时拷贝 1.3 fork常规用法 1.4 fork调用失败的原因 2 进程终止 2.1 进程退出场景 2.2 进程常见退出方法 3 进程等待 3.1 进程等待必要性 3.2 进程等待的方法 3.2.1 wait方法 3.2.2 waitpid方法 3.3 获取子进程status 4 进程程序替…

有趣的小知识(四)从基站到天线:深入了解如何优化网站速度的关键技术

一、全面认识基站 1.1 基站的定义 基站是一种通信设施&#xff0c;用于提供无线通信服务。它通常由一座塔、天线、收发信设备、电源和辅助设备等组成&#xff0c;可以与移动设备&#xff08;如手机、平板电脑等&#xff09;进行无线通信。基站是是无线终端(如手机)接入互联网…

寻找CSDN平行世界的另一个你

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 寻找CSDN平行世界的另一个你摘要前言列表测试目的摘要 本文作了一个测试&#xff0c;看看在 CSDN 的博文中&#xff0c;艾特&#xff08;&#xff09;某个好友&#xff0c;TA是否能够…

为一副通用纸牌设计数据结构

为一副通用纸牌设计数据结构 大家好&#xff0c;我是易安&#xff0c;今天我们来聊一道笔试题&#xff0c;这也是我曾经面试华为时做过的题&#xff0c;今天分享给大家。 题目&#xff1a; 如何设计一个通用的扑克牌数据结构&#xff1f;请解释如何继承它来实现特定的扑克游戏…

国内外人工智能AI工具网站大全(一键收藏,应有尽有)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 国内外人工智能AI工具网站大全&#xff08;一键收藏&#xff0c;应有尽有&#xff09;摘要一、AI写作工具二、AI图像工具2.1、常用AI图像工具2.2、AI图片插画生成2.3、AI图片背景移除…

分享10个前端开发者需要掌握的DOM技巧

Web开发不断发展&#xff0c;掌握最新的趋势和最佳实践对每位开发者来说都至关重要。Web开发的最重要方面之一就是使用文档对象模型&#xff08;DOM&#xff09;。在本文中&#xff0c;我们将探讨10个必须掌握的DOM技巧和技巧&#xff0c;配有代码示例&#xff0c;这将帮助您成…

Kotlin 是后端开发的未来

Kotlin 是后端开发的未来 严格类型、命名参数、多范式语言 您今天遇到的每个后端开发人员都会说他们使用 JavaScript、Python、PHP 或 Ruby 编写代码。近年来&#xff0c;您会遇到一小部分人转而使用 Kotlin 作为他们创建 Web 服务器的语言选择。由于我在学习Ktor&#xff0c;所…

项目部署---shell脚本自动部署项目

通过shell脚本自动部署项目 操作步骤&#xff1a; 在Linux中安装Git在Linux中安装maven编写shell脚本&#xff08;拉取代码、编译、打包、启动&#xff09;为用户授予执行shell脚本的权限执行shell脚本 执行过程&#xff1a;Linux服务器&#xff08;编译、打包、启动&#x…

巧用千寻位置GNSS软件|点测量状态栏与工具栏全解析

众所周知&#xff0c;点测量是提供点位坐标多种模式测量、测量模式切换、测量数据简单成图等多种方式的点位地理信息测量功能。下面我们来解析在千寻位置GNSS软件中点测量功能下的各状态栏和工具栏。图5.1-1点击【测量】->【点测量】&#xff0c;如图5.1-1 所示&#xff0c;…

面向削峰填谷的电动汽车多目标优化调度策略

说明书 MATLAB代码&#xff1a;面向削峰填谷的电动汽车多目标优化调度策略 关键词&#xff1a;电动汽车 削峰填谷 多目标 充放电优化 参考文档&#xff1a;店主自己整理的说明文档&#xff0c;公式、约束、数据齐全&#xff0c;可联系我查看 仿真平台&#xff1a;MATLAB YA…

Android 设置背景颜色透明度

前言 本章是对设计给出的颜色做透明度的处理 原因 一般情况下我们是不需要做处理的&#xff0c;那为什么又需要我们做透明度呢&#xff0c;原因就是咱们的设计小哥哥、小姐姐们没有自己做处理&#xff0c;如果处理了的话&#xff0c;我们直接使用设计标注的AHEX颜色就行&a…

Vue+echart 图根据网页自适应resize缩放

const chartBar null;data{return {chartBar :null} }//关键代码activated() {// 由于给echart添加了resize事件, 在组件激活时需要重新resize绘画一次, 否则出现空白bug// if (this.chartBar) {this.chartBar.resize();// }},chartBar echarts.init(document.getElementBy…

信息安全和网络安全

安全五要素&#xff1a; 机密 完整 并且能判断数据是否被篡改 可用 可控 可审查性 对于网络及网络交易&#xff0c;信息安全的基本需求是&#xff1a; 机密性完整性不可抵赖性 计算机系统安全保护的五个等级&#xff1a; 注释&#xff1a;其中的安全标记保护级是属于强…