【算法】Knuth-Morris-Pratt 算法(KMP算法):一种在字符串中查找子串的算法

引言

KMP(Knuth-Morris-Pratt)算法是一个在字符串中查找子串的算法,由 Donald Knuth、Vaughan Pratt 和 James H. Morris 共同发明。这个算法的特点是在查找过程中,不会回溯主串,也不会重复扫描已经比较过的子串,因此它的时间复杂度是线性的。它的主要优点是在比对过程中,当一个字符不匹配时,可以跳过一些无需再次比对的字符,从而提高匹配效率。


相关概念

模式串(Pattern String)

在字符串搜索算法中,"模式串"是你正在查找的特定字符串。例如,如果你在一本书中查找单词 “apple”,那么 “apple” 就是你的模式串。

主串(Main String)

相对的,"主串"是你在其中进行查找的大段文本。在上面的例子中,整本书的文本就是主串。

前缀(Prefix)

对于字符串str,其长度为k的前缀是指从第一个字符开始的长度为k的子串。

后缀(Suffix)

对于字符串str,其长度为k的后缀是指从最后一个字符开始的长度为k的子串。

部分匹配表(Partial Match Table)

部分匹配表(Partial Match Table,也称为next数组或者失败函数)是KMP算法的核心组成部分,它是对模式串进行预处理得到的一个数组。这个表用于记录模式串中每个位置之前的子串的最长公共前后缀的长度。


基本思想

KMP算法的主要思想是利用已经部分匹配的有效信息,使得后续的匹配可以跳过那些已知肯定不会匹配的字符。

KMP 算法的核心是一个叫做部分匹配表(Partial Match Table, PMT)或者失败函数的预处理过程。这个表格记录了子串的前缀集合与后缀集合的最长公共元素的长度。在查找过程中,如果发生字符不匹配,我们可以通过这个表格快速移动子串的位置,跳过一些不可能匹配的位置。

例如,对于字符串"ABCDABD",其部分匹配表如下:

字符ABCDABD
PMT0000120

这个表的意思是,在模式串中,位置i之前的子串的最长公共前后缀的长度是PMT[i]。例如,位置5之前的子串是"ABCDAB",它的最长公共前后缀是"AB",长度是2,所以PMT[5]=2。

当在主串中进行匹配时,如果遇到不匹配的字符,可以利用部分匹配表中的信息,将模式串向右滑动一定的距离,从而跳过一些肯定不会匹配的位置,提高匹配效率。

在计算部分匹配表时,通常会使用动态规划的思想,对每个位置,都检查它之前的所有子串,找出最长的那个公共前后缀。

这个部分匹配表在KMP算法中有一个非常重要的作用,那就是当模式串中的某个字符与主串不匹配时,可以根据这个表直接跳过一部分字符,而不需要一个一个地去比对。

通过这种方法,KMP 算法可以在 O(n+m) 的时间复杂度内完成字符串的查找任务,其中 n 是主串的长度,m 是子串的长度。


详细步骤

  1. 预处理阶段
    生成部分匹配表。对于每个位置 i,我们计算子串 s2[1...i] 的最长相等的真前缀和后缀的长度 pmt[i]。这个长度就是当 s2[i+1] 和主串的某个字符不匹配时,我们需要将子串移动到的位置。
	const int N = 1e7 + 7;
	string s1, s2;
	int pmt[N];
	cin >> s1 >> s2;
	s1 = " " + s1;
	s2 = " " + s2;
	int l1 = s1.length() - 1;
	int l2 = s2.length() - 1;
	int j;	// 当前已经匹配的字符数量

	// 生成部分匹配表
	j = 0;
	for (int i = 2; i <= l2; i++) {
		// 下一个字符不匹配
		while (j && s2[i] != s2[j + 1]) {
			// 向前回溯
			j = pmt[j];
		}
		// 下一个字符匹配
		if (s2[i] == s2[j + 1]) {
			// j 后移一位
			j++;
		}
		// 更新部分匹配表
		pmt[i] = j;
	}

  1. 查找阶段
    从左到右扫描主串,同时移动子串的位置。如果主串的某个字符和子串的当前字符不匹配,我们就通过部分匹配表移动子串的位置。具体的移动方法是,将子串的位置向右移动 i - pmt[i] 个位置,其中 i 是子串中最后一个与主串匹配的字符的位置。
	// 查找字符串
	j = 0;
	for (int i = 1; i <= l1; i++) {
		// 下一个字符不匹配
		while (j && s1[i] != s2[j + 1]) {
			// 向前回溯
			j = pmt[j];
		}
		// 下一个字符匹配
		if (s1[i] == s2[j + 1]) {
			// j 后移一位
			j++;
		}
		// 匹配到字符串
		if (j == l2) {
			cout << i - l2 + 1 << endl;
			// 向前回溯,继续查找
			j = pmt[j];
		}
	}

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

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

相关文章

2024年上海高考数学最后四个多月的备考攻略,目标140+

亲爱的同学们&#xff0c;寒假已经来临&#xff0c;春节即将到来&#xff0c;距离2024年上海高考已经余额不足5个月了。作为让许多学子头疼&#xff0c;也是拉分大户的数学科目&#xff0c;你准备好了吗&#xff1f;今天&#xff0c;六分成长为您分享上海高考数学最后四个多月的…

2024 高级前端面试题之 JS 「精选篇」

该内容主要整理关于 JS 的相关面试题&#xff0c;其他内容面试题请移步至 「最新最全的前端面试题集锦」 查看。 JS模块精选篇 1. 数据类型基础1.1 JS内置类型1.2 null和undefined区别1.3 null是对象吗&#xff1f;为什么&#xff1f;1.4 1.toString()为什么可以调用&#xff1…

燃烧的指针(三)

&#x1f308;个人主页&#xff1a;小田爱学编程 &#x1f525; 系列专栏&#xff1a;c语言从基础到进阶 &#x1f3c6;&#x1f3c6;关注博主&#xff0c;随时获取更多关于c语言的优质内容&#xff01;&#x1f3c6;&#x1f3c6; &#x1f600;欢迎来到小田代码世界~ &#x…

为什么需要使用线程池来创建线程?

当我们使用new Thread无限创建线程的时候 因为频繁的创建线程和销毁线程&#xff0c;cpu利用率会非常高 当cpu利用率达到100%的时候 那么没有可用的资源 让其他进程使用 那么其他进程访问就会导致卡顿 访问速度变慢 当我们使用线程池的时候 &#xff0c;cpu利用率就会降低&…

市场复盘总结 20240126

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 昨日主题投资 连板进级率 27/105 25.7% 二进三&#xff1a; 进级率低 50% 最常用的二种方法&#xff1a; 方…

2024最新版Visual Studio Code安装使用指南

2024最新版Visual Studio Code安装使用指南 Installation and Usage Guide for the Latest Visual Studio Code in 2024 By JacksonML Visual Studio Code最新版1.85已经于2023年11月由其官网 https://code.visualstudio.com正式发布&#xff0c;这是微软公司2024年发行的的最…

vs2019报错MSB4019 找不到导入的项目“BuildCustomizations\CUDA 9.2.props”

在VS中执行生成&#xff0c;报错如下&#xff1a;严重性 代码 说明 项目 文件 行 禁止显示状态 错误 MSB4019 找不到导入的项目“D:\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\BuildCustomizations\CUDA 9.2.props”。请确认 Import 声明“D:\Microso…

Mybatis----分页

1.什么是分页 分页&#xff08;Pagination&#xff09;是指将大量数据划分为多个页面进行展示的一种技术手段。在数据量较大的情况下&#xff0c;将所有数据一次性显示在页面上会导致加载时间过长和页面过于庞大&#xff0c;影响用户体验和系统性能。分页技术通过划分数据为多…

Mac Monitor:一款为macOS安全研究量身定制的高级独立系统监控工具

关于Mac Monitor Mac Monitor是一款功能强大的高级独立系统安全监控工具&#xff0c;该工具专为macOS安全研究、恶意软件分类和系统故障排除而设计&#xff0c;主要基于Apple Endpoint Security&#xff08;ES&#xff09;实现其功能。 Mac Monitor能够收集各种类型的系统事件…

量化交易学习2(因子研究)

因子有效性检验 参考1 参考2 在多因子研究框架中&#xff0c;因子的有效性检验是不可避免的工作&#xff0c;其本质是衡量一个因子的选股能力。 目前学术界和业界普遍使用的两种方法&#xff1a; 相关性检验 因子的相关性检验即检验单因子和收益率之间是否存在相关性 IC值 计…

搜狐新闻客户端使用Kotlin之后对JSON解析框架的探索

本文字数&#xff1a;7488字 预计阅读时间&#xff1a;45分钟 01 引言 自2017年Google发布Kotlin语言之后&#xff0c;Android开发由原来的Java开始向Kotlin过度&#xff0c;目前绝大部分Android开发岗位基本要求就是熟练使用Kotlin。事实上&#xff0c;很多有着多年历史的项目…

单片机学习笔记---矩阵键盘

目录 矩阵键盘的介绍 独立按键和矩阵按键的相同之处&#xff1a; 矩阵按键的扫描 代码演示 代码模块化移植 Keil自定义模板步骤&#xff1a; 代码编写 矩阵键盘就是开发板上右下角的这个模块 这一节的代码是基于上一节讲的LCD1602液晶显示屏驱动代码进行的 矩阵键盘的介…

主成分分析(PCA)Python

实际问题研究中&#xff0c;常常遇到多变量问题&#xff0c;变量越多&#xff0c;问题往往越复杂&#xff0c;且各个变量之间往往有联系。于是&#xff0c;我们想到能不能用较少的新变量代替原本较多的旧变量&#xff0c;且使这些较少的新变量尽可能多地保留原来变量所反映的信…

Idea Community社区版如何添加Run Dashboard

最近在学习spring cloud&#xff0c;跟着视频添加run dashboard&#xff0c;发现里面介绍的方法无法适用于idea community(社区版)。 然后自己研究了一下&#xff0c;成功添加&#xff0c;下面分享自己的方法。 如图&#xff0c;我的项目里添加了两个module&#xff0c;我想通…

【c语言】详解操作符(下)

前言&#xff1a; 在上文中&#xff0c;我们已经学习了 原码、反码、补码、移位 操作符、移位操作符、位操作符、逗号表达式、下标访问[ ]、函数调用&#xff08; &#xff09;&#xff0c;接下来我们将继续学习剩下的操作符。 1. 结构成员访问操作符 1.1 结构体成员的直接访…

79 C++对象模型探索。数据语义学 - 进程内存空间布局分析

不同的数据在内存中会有不同的保存时机&#xff0c;和保存位置&#xff0c;这一节就分析这个。 当运行一个可执行文件时候&#xff0c;操作系统就会把这个可执行文件加载到内存&#xff1b;此时进程有一个虚拟的地址空间&#xff08;内存空间&#xff09;&#xff0c;如下图&a…

Docker部署思维导图工具SimpleMindMap并实现公网远程访问

文章目录 1. Docker一键部署思维导图2. 本地访问测试3. Linux安装Cpolar4. 配置公网地址5. 远程访问思维导图6. 固定Cpolar公网地址7. 固定地址访问 SimpleMindMap 是一个可私有部署的web思维导图工具。它提供了丰富的功能和特性&#xff0c;包含插件化架构、多种结构类型&…

03_2 连续时间信号的傅里叶变换(FT) 非周期信号的傅里叶变换

各位看官&#xff0c;大家好&#xff01;本讲为《数字信号处理理论篇》03_2 连续时间信号的傅里叶变换 非周期信号的傅里叶变换。&#xff08;特别提示&#xff1a;课程内容为由浅入深的特性&#xff0c;而且前后对照&#xff0c;不要跳跃观看&#xff0c;请按照文章或视频顺序…

《30天自制操作系统》 第一周(D1-D7) 笔记

前言&#xff1a;这是我2023年5月份做的一个小项目&#xff0c;最终是完成了整个OS。笔记的话&#xff0c;只记录了第一周。想完善&#xff0c;却扔在草稿箱里许久。最终决定&#xff0c;还是发出来存个档吧。 一、汇编语言 基础指令 MOV: move赋值&#xff0c;数据传送指令…

nginx复现负载均衡案例

这里是下载好了docker&#xff0c;并显示了下镜像这里是拉到了nginx的镜像这里是把容器起来&#xff0c;-itd是容器关闭后销毁这里是显示起来的容器进入到这个容器里面查看许多命令用不了&#xff0c;应该想办法把docker里的文件夹映射到物理机中 这里是如果访问6666端口那么隧…