【算法思维】-- KMP算法

news2024/10/5 13:54:57

OJ须知:

  • 一般而言,OJ在1s内能接受的算法时间复杂度:10e8 ~ 10e9之间(中值5*10e8)。在竞赛中,一般认为计算机1秒能执行 5*10e8 次计算
时间复杂度取值范围
o(log2n)大的离谱
O(n)10e8
O(nlog(n))10e6
O(nsqrt(n)))10e5
O(n^2)5000
O(n^3)300
O(2^n)25
O(3^n)15
O(n!)

11

时间复杂度排序:o(1) < o(log2n) < o(n) < o(nlog2n) < o(n^2) < o(n^3) < o(2^n) < o(2^n) < o(3^n) < o(n!)


目录

字符串匹配算法

KMP算法

引出next数组

求next数组的练习

用手 + 看

用数学式

next数组的优化

引入nextval数组

复杂度分析


字符串匹配算法

        BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T 的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和 T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。(百度百科)

        接下来我们就将这段晦涩难懂的话,举一个例子:S:"ababcabcd",T:"abcd"。

  • 相等时:

  • 不相等时:

思路代码化展示: 

#include <cstdio>
#include <cassert>
#include <cstring>
int BF(const char* str, const char* sub)
{
	assert(str != nullptr && sub != nullptr);
	if (str == nullptr || sub == nullptr)
		return -1;
	int i = 0;
	int j = 0;
	int strLen = strlen(str);
	int subLen = strlen(sub);
	while (i < strLen && j < subLen)
	{
		if (str[i] == sub[j])
		{
			i++;
			j++;
		}
		else
		{
			//回退
			i = i - j + 1;
			j = 0;
		}
	}
	if (j >= subLen)
		return i - j;
	return -1;
}
int main()
{
	printf("%d\n", BF("ababcabcdabcde", "abcd"));
	printf("%d\n", BF("ababcabcdabcde", "abcde"));
	printf("%d\n", BF("ababcabcdabcde", "abcdef"));
	return 0;
}

KMP算法

        KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n) [1]。(百度百科)

#区别:KMP 和 BF 唯一不一样的地方在,主串的 i 并不会回退,并且 j 也不会移动到 0 号位置。

  • 首先举例,为什么主串不回退? 

        如果按照BF算法,那么必须i变为第二个字符,将变为第一个字符。但是我们可以知道都比到这个位置了,那么从 i 向前 j 向前的字符串一定是相等的。

        而根据KMP算法就是,先分析短的子字符串。

        是不是有一对,以j - 1结尾的字符串和0开头的子字符串相等。而根据i 向前 j 向前的字符串一定是相等可以知道。

        看似是巧合,但这就是核心!因为此时我们并不需要将i移动,并且已经比较了一段。

        而现在的问题就是: 如何知道,它该移到哪一个指定的位置?

引出next数组

        KMP 的精髓就是 next 数组:也就是用 next[j] = k;来表示,不同的 j 来对应一个 K 值, 这个 K 就是你将来要移动的 j 要移动的位置。

而 K 的值是这样求的:

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

#一句话:next[0] = -1,next[1] = 0,此后找以0开头j - 1结尾的两字串相等的长度。

求next数组的练习

  • 用手 + 看

练习 1:对于 "ababcabcd",求其的 next 数组?

练习 2:对于 "abcabcabcabcdabcde",求其的 next 数组?

-1 0 0 0 1 2 3 4 5 6 7 8 9 0 1 2 3 0

#Tip:增加一定只会 +1

  • 用数学式

        到这里相信大家对如何求next数组应该问题不大了,那么接下来的问题就是:已知next[i] = k;怎么求next[i+1] = ?;

        首先假设:next[i] = k 成立,那么就有这个式子成立: P[0]...P[k-1] = P[x]...P[i-1]; 

        并且由于长度的相等,所以x也是可以推算而出的: k - 1 - 0 = i - 1 - x ,所以带入x: P[0]...P[k-1] = P[i-k]...P[i-1]; 

        到这一步:我们再假设如果 P[k] == P[i]; 我们可以得到 P[0]...P[k] = P[i-k]..P[i]; 那这个就是 next[i+1] = k+1; 

         再来看看: Pk != Pi 的时候。

融汇贯通的理解:(为什么以此方式回退?)


逻辑思维转换图

 #一句话:k一直回退,直到找到p[i] == p[k],否者k = -1,然后next[所求] = k + 1。

//KMP算法
#include <cstdio>
#include <cassert>
#include <cstring>
#include <string>
#include <vector>
#include <iostream>

int KMP(std::string str, std::string sub)
{
	if (str.size() == 0 || sub.size() == 0)
		return -1;
	std::vector<int> next(sub.size(), 0);

	// 利用数学式求next
	next[0] = -1, next[1] = 0;
	for (int i = 1; i < sub.size() - 1; i++)
	{
		int k = next[i];
		while (sub[k] != sub[i])
		{
			k = next[k];
			if (k == -1) break;
		}
		next[i + 1] = k + 1;
	}

	int j = 0;
	int i = 0;
	while(i < str.size())
	{
        // j == -1 一开始就匹配失败了,那i++;j++;正好是sub重新开始,str下一个
		if (j == -1 || str[i] == sub[j])
		{
			i++;
			j++;
			if (j == sub.size()) return i - j;
		}
		else j = next[j];
	}
	return -1;
}
int main()
{
	printf("%d\n", KMP("ababcabcdabcdeebcd", "ebcd"));
	printf("%d\n", KMP("ababcabcdabcde", "abcde"));
	printf("%d\n", KMP("ababcabcdabcde", "abcdef"));
	return 0;
}

next数组的优化

        在上述的处理方式会出现下列情况。

        这一步一步回退不好,最好的就是一步就跳到第一个a,然后直接 -1 + 1 = 0,于是便有了next数组的优化,引入一个nextval数组。

引入nextval数组

nextval数组的求法:

  • 回退到的位置和当前字符一样,就写回退那个位置的nextval值。
  • 如果回退到的位置和当前字符不一样,就写当前字符原来的next值。

//KMP算法
#include <cstdio>
#include <cassert>
#include <cstring>
#include <string>
#include <vector>
#include <iostream>

int KMP(std::string str, std::string sub)
{
	if (str.size() == 0 || sub.size() == 0)
		return -1;
	std::vector<int> next(sub.size(), 0);
	std::vector<int> nextval(sub.size(), 0);

	// 利用数学式求next
	next[0] = -1, next[1] = 0;
	nextval[0] = -1;
	for (int i = 1; i < sub.size() - 1; i++)
	{
		int k = next[i];

		// 求nextval
		if (sub[k] == str[i]) nextval[i] = nextval[i - 1];
		else nextval[i] = next[i];

		while (sub[k] != sub[i])
		{
			k = nextval[k];
			if (k == -1) break;
		}
		next[i + 1] = k + 1;
	}

	int j = 0;
	int i = 0;
	while(i < str.size())
	{
        // j == -1 一开始就匹配失败了,那i++;j++;正好是sub重新开始,str下一个
		if (j == -1 || str[i] == sub[j])
		{
			i++;
			j++;
			if (j == sub.size()) return i - j;
		}
		else j = next[j];
	}
	return -1;
}
int main()
{
	printf("%d\n", KMP("ababcabcdabcdeebcd", "ebcd"));
	printf("%d\n", KMP("ababcabcdabcde", "abcde"));
	printf("%d\n", KMP("ababcabcdabcde", "abcdef"));
	return 0;
}

利用nextval优化求next效果:

复杂度分析

  • 时间复杂度:O(m+n),srt字符串长m、sub字符串长n。
  • 空间复杂度:O(n)

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

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

相关文章

某神QQ机器人BOT搭建教程win系统

某神QQ机器人BOT搭建教程win系统 大家好我是艾西&#xff0c;今天跟大家分享的是某神qi鹅群机器人bot搭建方式以及详细的操作步骤。跟上艾西的节奏准备发车啦&#xff01; 前言&#xff1a;&#xff08;xxxx即为https&#xff09;&#xff08;zzz即为com&#xff09; qi鹅群…

geoserver发布矢量切片服务理论与实战

geoserver发布矢量数据服务前几篇文章已经分享过了&#xff0c;但是在实际业务中&#xff0c;矢量数据shp文件有时候比较大&#xff0c;包含上百万完个点&#xff0c;发布完整的服务后&#xff0c;有时候&#xff0c;前端显示还是有点慢&#xff0c;毕竟是一次加载完成&#xf…

Verilog语法概述二:何为仿真?仿真可以在几个层面上进行?

Verilog 是一种用于数字逻辑电路设计的硬件描述语言&#xff0c;可以用来进行数字电路的仿真验证、时序分析、逻辑综合。 既是一种行为级&#xff08;可用于电路的功能描述&#xff09;描述语言又是一种结构性&#xff08;可用于元器件及其之间的连接&#xff09;描述语言。 …

day34_js

今日内容 零、 复习昨日 一、JS 零、 复习昨日 一、引言 1.1 JavaScript简介 JavaScript一种解释性脚本语言&#xff0c;是一种动态类型、弱类型、基于原型继承的语言&#xff0c;内置支持类型。它的解释器被称为JavaScript引擎&#xff0c;作为浏览器的一部分&#xff0c;广泛…

数据分类分级 数据识别-excel分类分级模版文件导入、解析

前面讲了数据分类分级 数据识别-实现部分敏感数据识别,本次针对模版导入展开,excel导入采用的是easyexcel 目录 easyexcel介绍easyexcel实战添加依赖读取数据监听器的实现数据读取方法读取结果上面图片是AI创作生成!如需咒语可私戳哦! easyexcel介绍 之前的excel导入解析…

全网最可”铐“最可“刑”的fiddler抓包教程

Fiddler 下载&#xff1a; https://telerik-fiddler.s3.amazonaws.com/fiddler/FiddlerSetup.exe 浏览器f12 选择“网络”&#xff0c;点选“保留日志” Fiddler 浏览器执行“去缓存刷新”&#xff1a;shiftf5 会话 即是 包 har文件在测试当中有什么作用?&#xff1a;h…

存储卡格式化后如何找回数据?一招轻松恢复数据

存储卡内存不足、存储卡中毒、存储卡损坏这几种情况都会导致我们把存储卡格式化操作&#xff0c;存储卡格式化后所有数据都会清空&#xff0c;这是众所皆知的&#xff0c;存储卡不小心格式化了怎么办&#xff1f; 其实是有方法恢复格式化的数据&#xff0c;我们可以通过专业的数…

香橙派4和树莓派4B构建K8S集群实践之五:端口公开访问配置

1. 应用场景说明 - 我们需用k8s集群提供开放特别的端口访问服务&#xff0c;如一些微服务端口 - 在团队开发环境中&#xff0c;通常要访问公共数据库&#xff0c;集群需提供统一的接口给大伙 接下来以实践四中设置的mariadb-galera为基础&#xff0c;公开端口3306&#xff0…

银豆信息张雪灿:钻石级合作伙伴的增长秘诀

编者按&#xff1a; 杭州银豆信息技术有限公司&#xff08;简称“银豆”&#xff09;&#xff0c;是一家专注于云计算服务的高科技企业&#xff0c;目前已为2000家企业级客户提供了专业的行业解决方案, 与人民网、光大银行、长安汽车金融、vivo金融、浙江省农科院、淄博市大数…

django部署在Centos7+python3+apache上教程

django在本地环境非常好配置使用自带的web服务就可以了但是部署到服务器上再使用自带的web就不方便了 一般是配合nginx或apache来使用。 这篇文章主要是教如何搭配apache的 1.升级sqlite3&#xff08;高版本django高版本不支持低版本sqlite3&#xff09; #一定要在安装python…

[ACTF新生赛2020]easyre 题解

1.查壳 32位文件&#xff0c;加了UPX壳 2.手动脱壳 使用Ollydbg UPX是一个压缩壳&#xff0c;运行了UPX将我们要运行的已经压缩的程序解压&#xff0c;才是真正的程序入口点OEP 我们需要将跟着汇编代码&#xff0c;找到程序真正的入口点 使用ESP定律可以快速定位 按下F7&…

Git设置代理

有时会国内会因为github克隆速度非常慢&#xff0c;中途各种错误断开造成克隆项目失败&#xff0c;可以尝试设置代理解决。 1、http、https协议 //设置全局代理 //http git config --global https.proxy http://127.0.0.1:1080 //https git config --global https.proxy http…

图神经网络:(节点分类)在KarateClub数据集上动手实现图神经网络

文章说明&#xff1a; 1)参考资料&#xff1a;PYG官方文档。超链。 2)博主水平不高&#xff0c;如有错误还望批评指正。 3)我在百度网盘上传了这篇文章的jupyter notebook。超链。提取码8888。 文章目录 文献阅读&#xff1a;代码实操&#xff1a; 文献阅读&#xff1a; 参考文…

一个由“API未授权漏洞”引发的百万级敏感数据泄露

2023年4月的某一天&#xff0c;腾讯安全专家Leo正在为某家医院的重保防护做第一轮的安全风险排查。 医院的专用APP是外部网络访问最高的&#xff0c;也就是最大的风险敞口&#xff0c;需要重点排查。 Leo下载APP进行测试后&#xff0c;发现该医院存在一个严重的问题&#xff…

图像复原与重建MATLAB实验

文章目录 一、实验目的二、实验内容1. 噪声图像及其直方图。2. 空间噪声滤波器。3. 逆滤波。 一、实验目的 了解一些常用随机噪声的生成方法。掌握根据指定退化函数对图像进行退化的方法。掌握当模糊图像只存在噪声时的几种滤波复原方法。掌握当模糊图像同时存在线性退化和噪声…

学会搭建小程序生鲜商城,开启生鲜电商新模式

电商平台的出现&#xff0c;为人们带来了极大的便利。然而&#xff0c;传统的电商平台已经不能满足消费者对于购物体验的要求。如今&#xff0c;小程序生鲜商城因其轻量化、高效率等特点&#xff0c;成为了众多卖家的首选。本文将介绍如何学会搭建小程序生鲜商城&#xff0c;并…

二分特训上------刷题部分----Week4(附带LeetCode特训)

二分特训上------理论部分----Week4(附带LeetCode特训)_小杰312的博客-CSDN博客 如果需要理论&#xff0c;请移步上一篇. /***** 注意&#xff1a;我们把 0000001111111模型中&#xff1a;0称呼为左边区间&#xff0c;1称呼为右边区间 (答案第一个1在右区间) 1111…

浅谈Redis

一、Redis的简介 1.开源免费的缓存中间件,性能高,读可达110000次/s,写可达81000次/s。 2.redis的单线程讨论&#xff1a; V4.0之前&#xff1a;是单线程的&#xff0c;所有任务处理都在一个线程内完成. V4.0&#xff1a;引入多线程&#xff0c;异步线程用于处理一些耗…

机器学习基础学习之线性回归

文章目录 首先从**目标函数**开始梯度下降法结合两个公式&#xff0c;让目标函数梯度下降多项式回归&#xff0c;多重回归解决办法&#xff1a;随机梯度下降 首先从目标函数开始 假设下图反映了 投入多少广告费&#xff0c;产生了多少销售量的关系 图中每个点都是一个数据&a…