KMP算法详讲(问题导向,通俗易懂)

        KMP算法是一种高效的字符串匹配算法,相比于BF算法的时间复杂度为O(n*m),它的时间复杂度降低到了O(n+m)。这种算法的高效性在于它利用了主串的指针不回溯,而只移动模式串的指针位置。然而,对于初学者来说,KMP算法并不容易理解。因此,本文将采用问题导向的方法,以通俗易懂的方式解释KMP算法。

        (注:本文主串与模式串字符的默认起始位置为1!)

      为什么BF算法的效率低下?

        我们先感性地匹配一下如下两个字符串(x表示未知字符):

主串:       xxxxaaabaaabxxxx

模式串:   aaabaaad

        此时主串的指针i 指向b,模式串的指针j指向d,两者此时“失配”了。根据BF算法的匹配方式,此时指针i将回溯到绿色的a,指针j回溯到第一个a.此时细心的同学可以发现,其实并不需要如此“大打出手”,因为回溯依旧无法匹配。其实,指针i可以保持不动,指针j只移动b位置上,让模式串头三个字符aaa直接匹配主串字符b前的三个字符aaa,此时才作后续的判断,匹配效率必定会更高一点.

        那么问题来了,此种匹配方式真的可信吗?如果真的可行,那么每次“失配”,指针j移动到什么位置呢?这是下文所需解决的问题.

        如何定位指针j的回溯位置?

        我们需要确定指针j前的子串的最长相同前后缀的长度len,那么j的回溯位置就是len+1.

        在开始如下内容前,务必搞清楚如下几个概念:

1.前缀:包含首位字符但不包含末位字符的子串.

2.后缀:包含末位字符但不包含首位字符的子串.

3.字符串的最长相同前后缀:

    若字符串为:abacab,那么它的前缀为:{"a","ab","aba","abac","abaca"},后缀为:{"b","ab","cab","acab","bacab"},显然最长相同前后缀为:"ab".

        为了方便解释原理,命名模式串指针j前的子串为sonString,sonString的最长相同前缀为:preString,最长相同后缀为:sufString.

       

               如上图所示,长条代表主串,短条代表模式串,此时h无法与g匹配,在红色长条中也存在最长相同的前后缀"ba"。此时为了提高匹配的效率,指针i保持不动,指针j指向a,如下图所示:

        

        很显然,当指针j所指向的字符出现“失配”的情况下,通过让最长前缀preString覆盖住最长后缀fudString,能够更高效地匹配模式串。同时,若preString的长度为len,那么指针j应该回溯到len+1.

        剩下的问题就是,如何求指针j的回溯位置了.

        如何求指针j的回溯位置?

        定义next数组,next[j]表示:当模式串的指针j所指向的字符与主串“失配”时,指针j应该回溯的位置,即next[j]代表j前面的子串的最长相同前后缀长度+1.

        若next[i] = k,那么它有如下意义:

字符串:a_{1}a_{2} \cdots a_{k-1}a_{k} \cdots a_{i+1-k}a_{i+2-k} \cdots a_{i-1}a_{i}

其中子串:a_{1}a_{2} \cdots a_{k-1}a_{i+1-k}a_{i+2-k} \cdots a_{i-1},前者为最长相同前缀,后者为最长相同后缀.

        给定模式串便能求算next数组,代码实现如下:

#include<iostream>
using namespace std;

const int N = 100;
int Next[N];
char mStr[N];
char tStr[N];

void GetNext(char ch[], int length) {
	Next[1] = 0;
	int i = 1, j = 0;
	while (i <= length) {
		if (j == 0 || ch[j] == ch[i]) {
			Next[++i] = ++j;
		}
		else {
			j = Next[j];	//	模式串的指针回溯位置
		}
	}
}

        求算next数组的代码很简短,它的原理十分奥秘,以下是它的推导过程:

        根据代码所示,我们规定Next[1] = 0,指针i指向sonString的最长相同后缀后面的第一个字符,指针j指向最长相同前缀后面的第一个字符,很显然,当ch[i]==ch[j]时,指针i+1后面的子串的最长相同前后缀的长度就是j+1.

        当ch[i]!=ch[j]时,假设k1 = Next[j],k2 = Next[k1],那么公式合理性推导如下:

        原sonString为:a_{1} \cdots a_{j-1} a_{j} \cdots a_{i-j+1} \cdots a_{i-1} a_{i},此时a_{1} \cdots a_{j-1} ==a_{i-j+1} \cdots a_{i-1},但是a_{j} !=a_{i}.

        因为k1 = Next[j],根据对称性,a1....a(k1-1) == a(j+1-k1)....a(j-1)==a(i+1-k1)....a(i-1),因在下一轮循环中,如果a[j]==a[i](此时j==k1)],那么a1....a(k1)==a(i+1-k1).....a(i),显然成立.

        若a[k1]!=a[i],那么重复j = Next[j],推导方式同上.

KMP算法的代码实现

#include<iostream>
using namespace std;

const int N = 100;
int Next[N];
char mStr[N];
char tStr[N];

void GetNext(char ch[], int length) {
	Next[1] = 0;
	int i = 1, j = 0;
	while (i <= length) {
		if (j == 0 || ch[j] == ch[i]) {
			Next[++i] = ++j;
		}
		else {
			j = Next[j];	//	模式串的指针回溯
		}
	}
}

int stringMatch() {
	int mLen = strlen(mStr + 1), tLen = strlen(tStr + 1);
	int i = 1, j = 1;
	while (i <= mLen && j <= tLen && i - j <= mLen - tLen) {
		if (mStr[i] == tStr[j]) {
			i++, j++;
		}
		else {
			j = Next[j];
		}

		if (j == 0) {
			j++, i++;	//	重新开始匹配
		}
	}
	if (j == tLen + 1) {
		return i - tLen;
	}
	else {
		return 0;
	}
}


int main() {
	while (1) {
		memset(Next, 0, sizeof(Next));
		cout << "输入主串与模式串:" << endl;
		cin >> mStr + 1;
		cin >> tStr + 1;
		GetNext(tStr, sizeof(tStr + 1));
		int pos = stringMatch();
		if (pos) {
			cout << "匹配成功,主串的匹配起始位置为:" << pos << endl;
		}
		else {
			cout << "匹配失败..." << endl;
		}
	}
	return 0;
}

        

        

        

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

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

相关文章

全面掌握:性能测试计划的制胜法宝

一&#xff0e;简介 简介部分就不用过多描述了&#xff0c;无非项目的背景&#xff0c;进行此次性能测试的原因&#xff0c;以及性能测试覆盖的范围等等&#xff0c;几乎所有项目文档都在开端对项目进行简单的阐述。 二&#xff0e;性能测试需求 寻找的被测试对象和压力点 …

windows 部署 weblogic 12.1.3

1、安装 1&#xff09;下载 地址&#xff1a;WebLogic Server 12c (12.2.1), WebLogic Server 11g (10.3.6) and Previous Releases 2&#xff09;安装 weblogic server java -Xmx1024m -jar fmw_12.1.3.0.0_wls.jar 出现图形界面按需配置&#xff0c;注意配置的安装路径不能…

11月编程榜最新出炉,第一名很离谱

这段时间&#xff0c;随着人工智能的崛起&#xff0c;Python的地位水涨船高。有不少朋友感觉到危机重重。 其中&#xff0c;最明显的&#xff0c;是市场环境的变化&#xff1a; 外部招聘&#xff1a;Python岗位日均需求量高达15000&#xff01;不仅是程序员&#xff0c;内容编…

【分享课】11月16日晚19:30PostgreSQL分享课:PG缓存管理器主题

PostsreSQL分享课分享主题: PG缓存管理器主题 直播分享平台&#xff1a;云贝教育视频号 时间&#xff1a;11月16日 周四晚 19: 30 分享内容: 缓冲区管理器结构 缓冲区管理器的工作原理 环形缓冲区 脏页的刷新

uniapp使用Canvas实现电子签名

来源&#xff1a; 公司的一个需求&#xff0c;需要给新注册的会员和客商需要增加签署协议功能&#xff1b; 之前的思路&#xff1a; 1、使用vue-signature-pad来实现电子签名&#xff0c;但是安卓手机不兼容&#xff1b; 2、uniapp插件市场来实现&#xff0c;但是对HBuilderX…

为什么小型企业应该拥抱数字化转型?

在当今飞速发展的商业环境中&#xff0c;数字化转型已经成为各种规模组织的必然选择。特别是小型企业&#xff0c;通过数字化转型&#xff0c;可以在保持竞争力、提高运营效率并开启新的增长机会方面获益匪浅。本文探讨了数字化转型的概念&#xff0c;强调了它对小型企业的重要…

测试小白必看:自动化测试入门基础知识

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

小型企业网络搭建方案

在这个日益数字化和连接的世界里&#xff0c;一个稳固的小型企业网络是实现高效运作的关键支柱。不论您是在经营一家初创公司还是小型企业&#xff0c;一个可靠的企业网络都是保证顺畅沟通、数据分享以及访问在线资源的重要因素。本篇文章将会引导您完成构建一个小型企业网络的…

C++入门第七篇--STL模板--vector模拟实现

前言&#xff1a; 有了前面的string库的介绍&#xff0c;在这里我就不再介绍vector库了&#xff0c;而是直接模拟实现了。 vector库的概念和作用&#xff1a; vector库是针对于数组的数据类型的容器&#xff0c;它有点类似我们曾经实现过的顺序表&#xff0c;你完全可以按照…

Google codelab WebGPU入门教程源码<6> - 使用计算着色器实现计算元胞自动机之生命游戏模拟过程(源码)

对应的教程文章: https://codelabs.developers.google.com/your-first-webgpu-app?hlzh-cn#7 对应的源码执行效果: 对应的教程源码: 此处源码和教程本身提供的部分代码可能存在一点差异。点击画面&#xff0c;切换效果。 class Color4 {r: number;g: number;b: number;a…

挑战字节软件测试岗,原来这么轻松...

当前就业环境&#xff0c;裁员、失业消息满天飞&#xff0c;好像有一份工作就不错了&#xff0c;更别说高薪了。其实这只是一方面&#xff0c;而另一方面&#xff0c;各大企业依然求贤若渴&#xff0c;高技术人才依然紧缺&#xff0c;只要你技术过硬&#xff0c;拿个年薪50w不是…

锁之间的故事

目录 常用锁策略 1.乐观锁 VS 悲观锁 2.轻量级锁 VS 重量级锁 3.自旋锁 VS 挂起等待锁 4.互斥锁 VS 读写锁 5.公平锁 VS 非公平锁 6.可重入锁 VS 可重入锁 CAS ABA问题 Synchronized原理 1. 锁升级/锁膨胀 2.锁消除 3.锁粗化 常用锁策略 1.乐观锁 VS 悲观锁 站在…

二叉树相关

一、概念 二、题目 2.1 把数组转换成二叉树 2.2.1 使用队列方式 public static Node getTreeFromArr2(int[] arr) {if (arr null || arr.length 0) {return null;}LinkedList<Node> quque new LinkedList<>();Node root new Node(arr[0]);quque.add(root);in…

有大量虾皮买家号想防关联该怎么做?

Shopee平台规定一个买家只能拥有一个买家号&#xff0c;如果一台电脑或者一个手机同时登录好几个买家号&#xff0c;那么很有可能就会关联封号的。那么有大量虾皮买家号想防关联该怎么做&#xff1f; 如果想要运用大量的shopee买家号来操作&#xff0c;那么需要使用有防指纹技术…

利用vscode连接远程服务器进行代码调试

文章目录 一、vscode下载二、连接服务器1. 安装remote development套件2. 配置ssh3. 连接服务器4. 打开服务器文件路径 三、支持GUI显示1. windows系统安装xserver服务&#xff1a;可以用xming或VcXsrv2. windows系统(安装了vscode的系统)下安装插件3. vscode实现免密登录远程服…

<蓝桥杯软件赛>零基础备赛20周--第6周--数组和队列

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周&#xff08;读者可以按…

Ansys Speos | 如何利用Speos联合optiSLang进行光导优化设计

在本例中&#xff0c;我们将使用 Speos 和 optiSLang 实现光导的设计优化&#xff0c;以实现汽车日行灯、内饰氛围灯等的光导设计&#xff0c;并改善光导亮度的均匀性&#xff0c;以自动优化设计的方式实现更好的照明外观。 概述 在汽车照明应用中&#xff0c;日行灯是一个独特…

[文件读取]Grafana未授权任意文件读取

1.1漏洞描述 漏洞编号Grafana未授权任意文件读取漏洞类型文件读取漏洞等级⭐⭐⭐漏洞环境VULFOCUS攻击方式 描述: Grafana是一个跨平台、开源的数据可视化网络应用程序平台。用户配置连接的数据源之后&#xff0c;Grafana可以在网络浏览器里显示数据图表和警告。 Grafana 存在…

【自定义列表头】vue el-table表格自定义列显示隐藏,多级表头自定义列显示隐藏,自由搭配版本和固定列版本【注释详细】

前言 功能介绍 最近遇到一个功能&#xff0c;需要的是把表格的列可以配置&#xff0c; 用户可以根据自己想要看的数据来改变表头列显示哪些隐藏哪些。 于是我做了两个版本。第一个版本是自由搭配的。 就是提前顶号所有的列&#xff0c;然后自己可以拖拽到想要位置顺序。 也可以…

打开IE浏览器

原文地址&#xff1a;https://www.xiaoheiwoo.com/windows-11-internet-explorer/#:~:text%E5%A6%82%E4%BD%95%E5%9C%A8%20Windows11%20%E4%B8%AD%E5%90%AF%E7%94%A8%20IE%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%843%E7%A7%8D%E6%96%B9%E6%B3%95%201%20%E6%96%B9%E6%B3%95%E4%B8%80…