KMP算法讲解与实现

news2025/4/17 16:43:11

0、概述

  • KMP是用于字符串查找/匹配的算法;

  • KMP算法的时间复杂度: O ( n ) O(n) O(n)

  • KMP算法的核心:

    • 1)如何理解 next 数组
    • 2)如何利用 next 数组加速匹配过程,优化时的两个实质
  • KMP算法的实现

1、引入

假设字符串 str 长度为 N N N,字符串 match 长度为 M M M M < = N M <= N M<=N,想确定 str 中是否有某个子串是等于 match 的。

【分析】

定义一个函数 int f(string s1, string s2),返回值表示从 s1 的哪个位置开始能找到完全匹配 s2 的子串,注意子串一定是连续的。

例:

s1 = "abc123def"
s2 = "123"
f(s1, s2) = 3
s1 = "abc123def"
s2 = "123e"
f(s1, s2) = -1

暴力过程:
请添加图片描述
如果 S1 的长度为 N N N,S2 的长度为 M M M,那么以 S1 的每个字符作为开头比较的时候,最多要比较 M M M 个字符,所以最差情况下,暴力比较的时间复杂度是 O ( N × M ) O(N \times M) O(N×M)

暴力的原因是前一个位置作为开头比较的结果没有办法给下一个位置作为开头去比较进行参考,每个位置作为开头进行字符串验证的操作都是独立的。

而 KMP 算法能做到 O ( N ) O(N) O(N) 的复杂度。

KMP算法是用于 字符串查找/匹配 的算法

2、KMP算法核心

2.1 理解next数组

首先,来看一个概念:前缀与后缀串的最长匹配长度
请添加图片描述
引出了 next数组
请添加图片描述
即 next 数组是记录的字符串的每个位置前缀和后缀串的最长匹配长度

2.2 利用next数组加速匹配过程及优化的实质

next 信息是对匹配字符串 match 求解的,next数组可以使得匹配过程加速。

  • next数组是如何加速匹配过程的呢?
    请添加图片描述
    而KMP算法是先得到 S2 字符串的 next 数组,当从S1的 i i i 位置作为开头进行匹配的时候,发现S1的 x x x 位置和 S2 的 y y y 位置不匹配的时候,借助S2的 next 数组,找到S1的 j j j 位置,然后将图中的 z z z 位置和 x x x 位置进行比较即可:
    请添加图片描述
  • 优化的两个实质
    直接比较 x x x z z z 位置的实质:
    1、 j j j 位置开始的字符串能否匹配成功S2;
    2、 i i i j j j 位置的字符以其中任意一个作为开头都无法与 S2 成功匹配,所以直接舍弃;

实例1:
请添加图片描述
过程:一开始 S1 的 i i i 位置和 S2 的 0 位置对齐,逐一进行匹配,发现S1的 t t t 字符和 S2的 5 位置的字符不匹配,而 S2 的 5 位置的信息是 2,通过后缀串的第一个字符找到 S1的 j j j 位置,然后 S2字符串向前推直到 S2的0位置和 S1的 j j j 位置对齐,然后直接比较S2 的 2 位置字符 与 S1的 t 字符即可;以此类推。

实例2:
请添加图片描述

两个加速中,第①个不言而喻,定义就是如此;

那么接下来证明第②个:为什么 i i i j j j 位置的任意一个字符作为开头匹配不出 S2?

请添加图片描述

前提:从 i i i 位置开始逐一匹配,直到 x x x 位置匹配失败,利用 y y y 位置的信息找到 S1 中的 j j j 位置。

假设:从 i i i j j j k k k 位置出发能成功匹配到 S2 (即 k k k 位置和 S2 的 0 位置对应,往后逐一匹配)

那么: k k k x x x 前的这一段和 S2 等量的前缀一样,而这一段也是 y y y 的等量后缀。

矛盾: y y y 之前的字符串存在一个更长的后缀和前缀相等的情况,与求解的 y y y 位置的信息矛盾,如果求解的 y y y 位置的信息是正确的,那么这种情况就不可能发生!进而证明了从 i i i j j j 的任意位置出发都不可能匹配出 S2。

S2往右推的操作的实现:就是跳到当前位置的 next 数组中的值的位置即可,用 next 数组的值对应的字符和未匹配成功 S1 的位置进行比较:
请添加图片描述

3、KMP算法的实现

如何快速得到next数组?

任意字符串的0位置信息一定是-1;1位置信息一定是0;如果0和1位置字符相同,则2位置的信息为1,否则为0
如果next数组是从左往右依次求好的,那么 i i i 位置的信息能否用之前的信息加速得到?

流程: i i i 位置的信息和该位置本身的字符无关,然后看 i − 1 i-1 i1 位置的信息
请添加图片描述
注意:不管往左跳到哪里,前缀的后一个字符都是和待求位置的前一个位置字符相比。

public class KMP {
	//整体时间复杂度O(N)
	public static int getIndexOf(String s1, String s2) {
		//过滤无效条件
		if (s1 == null || s2 == null || s2.length() < 1 || s1.length() < s2.length()) {
			return -1;
		}
		char[] str1 = s1.toCharArray();
		char[] str2 = s2.toCharArray();
		//不需要开头位置
		int x = 0; //s1中比对到的位置
		int y = 0; //s2中比对到的位置
		// 时间复杂度O(M),而 m <= n
		int[] next = getNextArray(str2); //求解s2的next数组
		// O(N)
		// 复杂度证明:
		// x<=n,而y<=m, m<=n
		// x的最大值为n,而x-y的最大值也是n
		// 循环的第1个分支,x变大,y变大,所以x-y不变
		// 循环的第2个分支,x变大,y不变,所以x-y变大
		// 循环的第3个分支,y变小,x不变,所以x-y变大
		// 也就是说x和x-y这两个量都不会减小,最多也只能到n,所以三个分支的发生次数<=2n,即 O(n)
		// 技巧:因为x和y有时候增加有时候减小,所以数学上作除或作差(x-y)评估整体的变化幅度
		while (x < str1.length && y < str2.length) { //匹配过程
			if (str1[x] == str2[y]) { //如果相等
				x++;
				y++;
			} else if (next[y] == -1) { // 就是y == 0
			 	//s2中只有0位置信息是-1,意味着不能再往左跳了,所以s1换下一个位置来比较吧
				x++;
			} else { //当前的两个比对的字符没有匹配上,且y还能往左跳
				y = next[y];
			}
		} 
		//如果循环条件终止的时候y越界了,说明s1中一定有以某个字符开头能匹配成功s2
		//如果y越界了,x - y就找到了以其开头能匹配成功的位置;否则不存在这样的位置,匹配不成功
		return y == str2.length ? x - y : -1;
	}
	
	public static int[] getNextArray(char[] str2) {
		if (str2.length == 1) { //任意字符串的0位置的信息都是-1
			return new int[] { -1 };
		}
		int[] next = new int[str2.length];
		next[0] = -1;
		next[1] = 0;
		int i = 2; // 目前在哪个位置上求next数组的值
		
		int cn = 0; // 当前是哪个位置的值再和i-1位置的字符比较,前缀的下一个字符位置
		//next数组中的信息既表示前缀和后缀最长匹配长度,也是前缀的下一个字符的位置
		//当cn跳到一个位置和i-1位置的字符相同时,i位置的信息就是cn+1
		//当i=2时,i-1位置是1,对于 2 位置来说,就是需要0位置和1位置比较,所以一开始cn=0

		//循环的复杂度求解:
		//因为循环中涉及两个变量i和cn,但是有的分支中cn在变小,出现回退行为,有的cn变大,导致不确定变化幅度,于是作差i-cn
		//两个变量:i<=m, i-cn<=m
		//第1个分支:i变大,cn变大,i-cn不变
		//第2个分支:i不变,cn变小,i-cn变大
		//第3个分支:i变大,cn不变,i-cn变大
		//三个分支是互斥的,利用三个分支发生的极限来估计while执行的次数
		//i和i-cn都不变变小,各自最多到m,所以三个分支发生的次数最多2m,于是时间复杂度O(m)
		while (i < next.length) {
			if (str2[i - 1] == str2[cn]) { // 配成功的时候
				next[i++] = ++cn; 
				//使用++cn而不是cn+1的写法
				//是因为到计算i+1位置的信息时,首先使用的是i位置的信息,就是cn+1的结果,此处使用++cn刚好就能得到正确的结果
				//这句代码的功能就是:
				//既设置了i位置该有的next数组的值,
				//也成功地让下一个位置正好使用现在设置的信息去完成它的匹配工作 
			} else if (cn > 0) { //cn还能继续往左跳
				cn = next[cn];
			} else {
				next[i++] = 0;
			}
		}
		return next;
	}

	// for test
	public static String getRandomString(int possibilities, int size) {
		char[] ans = new char[(int) (Math.random() * size) + 1];
		for (int i = 0; i < ans.length; i++) {
			ans[i] = (char) ((int) (Math.random() * possibilities) + 'a');
		}
		return String.valueOf(ans);
	}

	public static void main(String[] args) {
		int possibilities = 5;
		int strSize = 20;
		int matchSize = 5;
		int testTimes = 5000000;
		System.out.println("test begin");
		for (int i = 0; i < testTimes; i++) {
			String str = getRandomString(possibilities, strSize);
			String match = getRandomString(possibilities, matchSize);
			if (getIndexOf(str, match) != str.indexOf(match)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("test finish");
	}

}

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

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

相关文章

推荐|资深架构师 10 年 10 条干货职场心得

出道这么些年&#xff0c;跳过好多公司&#xff0c;除了国企其他的基本上都去过&#xff0c;甲方、乙方、外包、外企、互联网公司、创业公司…总结下来有这么些体会&#xff0c;希望能对你和大家有些参考价值。1. 保持学习可能离开了学校之后&#xff0c;所有的学习几乎都出于工…

b站黑马的Vue快速入门案例代码——图片切换(类似手动播放的轮播图)

目录 目标效果&#xff1a; 重点原理&#xff1a; 1.用数组储存图片的数据 2.v-bind指令可以设置元素属性 e.g.src 语法 v-bind:属性名表达式 简写【实际开发常用】 :属性名表达式 3.v-show和v-if都可以切换元素的显示/隐藏状态 (1)频繁切换显示/隐藏的dom元素用 v…

Opencv项目实战:17 贪吃蛇游戏

目录 0、项目介绍 1、效果展示 2、项目搭建 3、项目代码展示与讲解 4、项目资源 5、项目总结 0、项目介绍 这次是一个有意思的计算机视觉游戏——贪吃蛇&#xff0c;我们以食指为蛇头&#xff0c;不断的移动我们的手指&#xff0c;当吃到甜甜圈的时候&#xff0c;蛇身增…

浅谈字节码增强技术系列2-Asm与Cglib

作者&#xff1a;董子龙 前言 记得那是2022年秋天的第一场雨&#xff0c;比2021年来的稍晚一些&#xff0c;在那个秋雨朦胧的下午&#xff0c;正在工位上奋笔疾书的我突然听到了前面波哥对着手机听筒说出来的"温柔"的话语&#xff1a;说说你了解的spring-aop。话音…

基于Vision Transformers的文档理解简介

文档理解是从pdf、图像和Word文档中提取关键信息的技术。这篇文章的目标是提供一个文档理解模型的概述。 文档理解算法使用编码器-解码器结构分析文档内容&#xff0c;该管道结合了计算机视觉(CV)和自然语言处理(NLP)方法。管道的CV部分将文档作为输入图像进行分析&#xff0c…

对数据库索引的理解以及索引在MySQL中的数据结构

引言 索引在本质上相当于书的目录&#xff0c;通过目录就可以快速的找到某个章节对应的位置。索引的效果&#xff0c;就是加快了查找的速度。日常进行数据库的操作&#xff0c;一般地都是进行增删查改&#xff0c;而在很多场景中&#xff0c;进行查找的概率要比增删改大很多。…

搭建Flutter Web开发调试环境

Setting up the Framework/Engine development environment背景搭建 framework 开发环境修改调试 framework 源码运行 framework 测试用例同步更新 framework 源码搭建 engine 开发环境准备 depot_tools部署 engine 源码编译 engine 源码修改调试 engine 源码指定 --local-engi…

​极氪汽车被曝拟美股上市:最高募资10亿 李书福接连收获IPO

雷递网 雷建平 12月13日极氪汽车日前被曝出已“秘密递交申请”&#xff0c;准备在美股上市&#xff0c;此次拟募资10亿美元&#xff0c;估值可能高达100亿美元。有分析认为&#xff0c;极氪汽车秘密递交招股书&#xff0c;是为尽可能向其他竞争对手隐藏招股书细节&#xff0c;以…

十二、对象继承深入、call_apply、※圣杯模式、※构造函数和闭包、※企业模块化

十二、对象继承深入、call_apply、※圣杯模式、※构造函数和闭包、※企业模块化 对象继承深入 原型链继承 原型链定义 ​ 对象沿着__proto__在原型上寻找属性形成一种链条式的继承关系&#xff0c;这种继承关系就叫做原型链。 例如&#xff1a; Professor.prototype {na…

Linux(二)vim编辑器,gcc,库

vim 简介 vi是“visual interface”的简称。 类似于Windows下的记事本。 vim可以视为vi的高级版本 按下vimtutor进入帮助文档&#xff0c;以下内容在其中都有 vim的三种模式 Vi有三种基本工作模式&#xff1a;命令模式、文本输入模式、末行模式 基本操作 命令模式下的操作…

迷宫--dfs解法以及迷宫问题要不要回溯

文章目录题意题解思路&#xff1a;问题&#xff1a; 迷宫问题dfs要不要回溯&#xff1f;题意 一天Extense在森林里探险的时候不小心走入了一个迷宫&#xff0c;迷宫可以看成是由 n∗n 的格点组成&#xff0c;每个格点只有2种状态&#xff0c;.和#&#xff0c;前者表示可以通行…

装载问题 ——分支限界法(Java)

装载问题 ——分支限界法&#xff08;Java&#xff09; 文章目录装载问题 ——分支限界法&#xff08;Java&#xff09;1、 问题描述2、算法设计3、算法的改进4、程序代码5、参考资料1、 问题描述 有一批共n个集装箱要装上2艘载重量分别为C1和C2的轮船&#xff0c;其中集 装箱…

数图互通高校房产管理——教职工住宅方案

数图互通房产管理系统在这方面做得比较全面&#xff1b; 1、住房管理 1.1 住房档案 住房模块的管理主要是针对学校的承租住宅和已售住宅的管理&#xff0c;用于登记已售住宅的产权人信息&#xff0c;记录承租住宅的租赁起止日期、月租金等基本信息。 支持住房的坐落信息、楼栋…

Cas:146368-11-8(水溶)|Sulfo CY5-羧酸|Cyanine5 Carboxylic Acidic acid

Cas:146368-11-8(水溶)|Sulfo CY5-羧酸|Cyanine5 Carboxylic Acidic acid Sulfo CY5-羧酸这种分子可以被认为是非活性染料&#xff0c;用于控制样品和仪器校准。为了与胺和蛋白质标记偶联&#xff0c; 中文名&#xff1a;Sulfo CY5-羧酸 英文名&#xff1a;Cyanine5 Carboxy…

rate-limit 一款 java 开源渐进式分布式限流框架使用介绍

项目简介 rate-limit 是一个为 java 设计的渐进式限流工具。 目的是为了深入学习和使用限流&#xff0c;后续将会持续迭代。 特性 渐进式实现 支持独立于 spring 使用 支持整合 spring 支持整合 spring-boot 内置多种限流策略 快速开始 需求 jdk 1.7 maven 3.x mav…

ARM S5PV210 时钟系统与时钟体系框图

前言 This chapter describes the clock management unit (CMU) supported by S5PV210. The system controller (SYSCON) manages CMU and power management unit (PMU) in S5PV210. 本章介绍 S5PV210 支持的时钟管理单元&#xff08;CMU&#xff09;。系统控制器&#xff08…

【软考】系统集成项目管理工程师(十一)项目人力资源管理

一、项目人力资源管理概述二、激励理论1. 马斯洛需求层次理论2. 赫茨伯格的双因素理论3. X 理论/ Y 理论4. 期望理论三、人力资源管理子过程1. 规划人力资源管理2. 组建项目团队3. 建设项目团队4. 管理项目团队一、项目人力资源管理概述 在了解人力资源管理之前,我们先来认识…

从局部到全局:语义相似度的测地线距离

©PaperWeekly 原创 作者 | 苏剑林单位 | 追一科技研究方向 | NLP、神经网络前段时间在最近的一篇论文《Unsupervised Opinion Summarization Using Approximate Geodesics》[1] 中学到了一个新的概念&#xff0c;叫做“测地线距离&#xff08;Geodesic Distance&#xff…

.net开发安卓入门 - Service (服务)

.net开发安卓入门 - Service Android Service 概述Service VS Thread &#xff08;服务和线程之间进行选择&#xff09;前台服务代码启动前台服务方法运行效果后台服务代码启动代码绑定服务AIDL同系列文章推荐Android Service 概述 移动应用不像桌面应用。 桌面具有大量资源&a…

基于51单片机的正弦波发生器设计

程序运行图&#xff1a; 仿真原理图&#xff1a; 部分程序&#xff1a; #include <reg52.h> //接口定义 sbit DA P1^1; sbit CK P1^2; sbit CS P1^4; //10bit取样&#xff0c;1024点正弦查表数据 unsigned int code sine_dot[1024] { 0x200,0x203,0x206,0x209,…