字符串匹配算法之BF与KMP算法

目录

BF算法(暴力匹配算法)

KMP算法

核心思想:

next数组

next数组的优化


BF算法(暴力匹配算法)

#include <assert.h>
int BF(const char* str, const char* sub)
{
	assert(str != NULL && sub != NULL);
	if (str == NULL || sub == NULL)
	{
		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; //没有匹配返回-1
}

int main()
{
	printf("%d\n", BF("ababcabcdabcde", "abcd")); //5
	printf("%d\n", BF("ababcabcdabcde", "abcde")); //9
	printf("%d\n", BF("ababcabcdabcde", "abcdef")); //-1
	return 0;
}

KMP算法

核心思想:

KMP算法是在BF算法基础上的优化,BF算法中匹配不成功时i和j都要回退,而KMP算法的核心就是匹配失败时,i不回退,而j回退到特定的位置(不一定是0号位置了),然后继续匹配!

问题:为啥i可以不回退, j也不一定非要回退到0号位置??

有了上面的分析,我们的目标就已经很明确了,就是要计算子串的每个位置匹配失败时,j应该回退到哪一个位置,于是就有了我们的next数组!

next数组

next[i] 记录的就是子串匹配到 i 位置时 匹配失败后 i 应该回退的下标

而next数组如何求解呢??? 比如next数组中 i 下标对应的值是几呢??? 那我们只需要看子串中 [0, i-1]这段区间前缀 和 后缀相等的字符串的最长长度, 假如是k, 那么next[i] = k

子串的第一个位置和第二个位置匹配失败时,该位置之前绝不可能用公共子串,所以next[0]和next[1]都应该是0, 但是为了方便后续代码处理,我们将next[0]置成-1

我们现在已经明白了next数组的做用和求法,但问题是上面的next数组是我们肉眼观察求得的,可是计算机并没有上帝视角,如何编程求得next数组呢???

假设已知next[i] = k, 我们能否求得next[i+1]等于多少呢??? 如果可以求出来,由于next[0]和next[1]都是已知的,所以我们只需要从第三个位置开始for循环即可求得next数组~

问题: 已知next[i] = k, 求next[i+1] = 多少??

1.假设p[i] == p[k], 那么next[i+1] = k+1   (p是patten, 指的是子串, 也叫模式串)

证明:

next[i] = k, 说明 p[0] 到 p[k-1] 与 p[x] 到 p[i-1] 这两段字符串是一样的,根据两段字符串长度一样可以求得x,  (k-1-0)+1 = (i-1-x)+1, 求得x=i-k, 所以 p[0]...[k-1] == p[i-k]...p[i-1]

而p[i] == p[k], 则 p[0]...[k] == p[i-k]...p[i], 即 next[i+1] = k+1

2.假设p[i] != p[k], 此时需要做的就是让k回退即可,只需要让k = next[k],一直回退到 p[i] == p[k], 此时next[i+1] = k+1了!!!

如果回退到了0位置,p[i] 仍然不等于 != p[k],  k再 = next[k], k就 == -1了, 此时如果再判断 p[i] == p[k] 就会越界! 所以如果k == -1了,表明next[i+1]也就是0,刚好是k+1, 这就是为啥把next[0]设置成-1的原因, 然后i++, k++继续填充next数组即可

到此为止,我们就把kmp算法的核心都讲解完了,重点就是kmp算法的核心思想与next数组的原理与求法

#include <assert.h>
#include <stdio.h>
void GetNext(int* next, const char* sub)
{
	int lensub = strlen(sub);
	next[0] = -1;
	next[1] = 0;
	int i = 2;//下一项
	int k = 0;//前一项的K
	while (i < lensub)//next数组还没有遍历完
	{
		//注意,讲解原理时我们假设已知next[i],求next[i+1], 但写代码时next[i]是我们要求解的,因此已知next[i-1]
		if ((k == -1) || sub[k] == sub[i - 1]) 
		{
			next[i] = k + 1;
			i++;
			k++;
		}
		else
		{
			k = next[k]; //k回退
		}
	}
}

int KMP(const char* s, const char* sub, int pos) //pos表示从主串的pos位置开始匹配
{
	int i = pos;
	int j = 0;
	int lens = strlen(s);
	int lensub = strlen(sub);
	int* next = (int*)malloc(lensub * sizeof(int));//和子串一样长
	assert(next != NULL);
	GetNext(next, sub);
	while (i < lens && j < lensub)
	{
		if ((j == -1) || (s[i] == sub[j])) 
		{
			i++;
			j++;
		}
		else
		{
			//如果子串的第一个位置字符就和主串不匹配,那么j就会直接变成-1,然后进入if, 此时让j++来到0号位置,i++来到下一个位置继续匹配即可
			j = next[j]; 
		}
	}
	free(next);
	if (j >= lensub)
	{
		return i - j;
	}
	else
	{
		return -1;
	}
}

int main()
{
	const char* str = "ababcabcdabcde";
	const char* sub = "abcd";
	printf("%d\n", KMP(str, sub, 0)); //5
	return 0;
}
next数组的优化

如果子串中,有大量的重复元素时,next数组就可以优化,因为假设6号下标匹配失败,回退到next[6]也就是5号下标, 此时字符仍然是'a', 依旧会匹配失败,还需要继续回退!!!

 所以我们可以将next数组优化成nextval数组,nextval数组可以根据next数组求得

1.回退到的位置和当前字符一样,就写回退那个位置的nextval值

2.回退到的位置和当前字符不一样,就写当前字符原来的next值

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

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

相关文章

962: 括号匹配问题

【学习版】 【C语言】 【C】 #include<iostream>class MyStack { public:struct Node {char val;Node* prev;Node* next;Node(char x) :val(x), prev(NULL),next(NULL) {};};MyStack() {base new Node(0);top base;}bool empty() {return top base;}void push(int …

C++类与对象下(个人笔记)

类与对象下 1.构造函数补充1.1构造函数体赋值1.2初始化列表1.3explicit关键字 2.static成员2.1特性 3.友元3.1友元函数3.2友元类 4.内部类5.匿名对象6.拷贝对象的一些优化7.笔试题 1.构造函数补充 1.1构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xf…

在数字化转型的背景下,如何构建高效的数据资产管理体系?

在数字化转型的大潮中&#xff0c;数据已成为企业创新发展的重要驱动力。如何高效地管理这些数据资产&#xff0c;不仅关系到企业的日常运营&#xff0c;更直接决定了企业能否在激烈的市场竞争中脱颖而出。对于企业管理者或首席信息官&#xff08;CIO&#xff09;而言&#xff…

大学英语ab级题搜题软件?分享7个支持答案和解析的工具 #笔记#其他

合理利用学习辅助工具和资料&#xff0c;可以帮助大学生更好地组织学习内容、掌握知识点和提升学术水平。 1.智能翻译官 这是一款多语言在线翻译神器&#xff0c;除了最基础的英语以外&#xff0c;还支持日语、德语、俄语、法语等几十种语言文本翻译和拍照翻译&#xff0c;并…

一文搞懂从爬楼梯到最小花费(力扣70,746)

文章目录 题目前知动态规划简介动态规划模版 爬楼梯一、思路二、解题方法三、Code 使用最小花费爬楼梯一、思路二、解题方法三、Code 总结 在计算机科学中&#xff0c;动态规划是一种强大的算法范例&#xff0c;用于解决多种优化问题。本文将介绍动态规划的核心思想&#xff0c…

积木-蓝桥每日真题

0积木 - 蓝桥云课 (lanqiao.cn) 题目描述 小明用积木搭了一个城堡。 为了方便&#xff0c;小明在搭的时候用的是一样大小的正方体积木&#xff0c;搭在了一个n行m列的方格图上&#xff0c;每个积木正好占据方格图的一个小方格。 当然&#xff0c;小明的城堡并不是平面的&#x…

2014最新AI智能系统ChatGPT网站源码+Midjourney绘画网站源码+搭建部署教程文档

一、文章前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧。已支持…

2.SpringBoot利用Thymeleaf实现页面的展示

什么是Thymeleaf&#xff1f; Thymeleaf是一个现代服务器端Java模板引擎&#xff0c;适用于Web和独立环境&#xff0c;能够处理HTML&#xff0c;XML&#xff0c;JavaScript&#xff0c;CSS甚至纯文本。 Thymeleaf的主要目标是提供一种优雅且高度可维护的模板创建方式。为实现这…

代码审计sql注入部分函数绕过方法

目录 1.宽字节注入 2.预编译模式下的sql注入 3无法预编译的 1.3.1. like关键字 1.3.2.不能加单引号 4.相关实战实战 4.1.某个业务网站具有sql注入 4.2.梦想cms代码审计 5.相关参考资料 1.宽字节注入 <?php $dbinit_db(); $db->query("set SET NAMESgbk);…

Thonny 开发环境下使用PICO系列教程2----点亮板载灯3S后熄灭

Thonny 开发环境下使用PICO系列教程2----点亮板载灯3S后熄灭 硬件代码 硬件 链接: 官网地址 参考原理图可以发现&#xff0c;PICO板载灯连接的是GP25引脚 代码 // 板载灯点亮3秒后熄灭 import board //想要控制PICO的引脚就要引入board import time//延迟 from digitali…

【QT入门】Qt自定义控件与样式设计之QPushButton常用qss

往期回顾 【QT入门】Qt自定义控件与样式设计之qss介绍(Qt style sheet)-CSDN博客 【QT入门】 Qt自定义控件与样式设计之qss选择器-CSDN博客 【QT入门】 Qt自定义控件与样式设计之QLineEdit的qss使用-CSDN博客 【QT入门】Qt自定义控件与样式设计之QPushButton常用qss 这里我们主…

数据结构__顺序表

概念及结构 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储。在数组上完成数据的增删查改 需要用到数组&#xff1a;数组的绝对优势&#xff1a;下标的随机访问&#xff08;因为物理空间连续&#xff09; a[i]等…

政安晨【AIGC实践】(一):在Kaggle上部署使用Stable Diffusion

目录 简述 开始 配置 执行 安装完毕&#xff0c;一键运行 结果展示 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 人工智能数字虚拟世界实践 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提…

2024.4.8-day12-CSS 常用样式属性和字体图标

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 作业2024.4.8-学习笔记盒子阴影文本阴影透明的vertical-align字体使用 作业 &…

2024年网络安全趋势前瞻:从AI攻击到云安全新挑战

随着2024年开展新的序幕&#xff0c;网络安全领域正面临着前所未有的挑战与机遇&#xff0c;一系列引人注目的趋势和预测逐渐浮出水面。 一、AI技术发展引发的安全问题 近年来&#xff0c;我们见证了AI技术的飞速进步&#xff0c;其中ChatGPT等引领潮流的AI服务成为公众瞩目的…

鸿蒙OS实战开发:【多设备自适应服务卡片】

介绍 服务卡片的布局和使用&#xff0c;其中卡片内容显示使用了一次开发&#xff0c;多端部署的能力实现多设备自适应。 用到了卡片扩展模块接口&#xff0c;[ohos.app.form.FormExtensionAbility] 。 卡片信息和状态等相关类型和枚举接口&#xff0c;[ohos.app.form.formInf…

C++要点细细梳理——trivial:运算符优先级、switch、临时变量默认赋值等

1. 运算符优先级 在C语言中&#xff0c;运算符的优先级决定了在表达式中各个运算符的执行顺序。当一个表达式中有多个运算符时&#xff0c;优先级高的运算符会先被计算。如果两个运算符的优先级相同&#xff0c;那么它们的结合性&#xff08;从左到右或从右到左&#xff09;会决…

【优选算法专栏】专题十六:BFS解决最短路问题(二)

本专栏内容为&#xff1a;算法学习专栏&#xff0c;分为优选算法专栏&#xff0c;贪心算法专栏&#xff0c;动态规划专栏以及递归&#xff0c;搜索与回溯算法专栏四部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握算法。 &#x1f493;博主csdn个人主页&#xff1a;小…

主从复制、数据持久化 、Redis主从集群、哨兵机制 、Redis分片集群

数据持久化 Redis、主从集群、哨兵机制 Redis分片集群 1、单点 redis 的问题2、主从复制2.1 命令传播 3、Redis的持久化3.1 AOF3.2 RDB&#xff08;默认方式&#xff09;RDB 方式&#xff1a;执行快照时&#xff0c;数据能被修改吗&#xff1f;RDB 方式总结 3.3 RDB 和 AOF 组合…

ORAN C平面 Section Extension 22

ORAN C平面Section扩展22用于ACK/NACK请求。除section type 7外&#xff0c;section扩展22可以用于从O-DU发送到O-RU的所有section type和section扩展。 对于一个section描述&#xff0c;O-DU可以使用section扩展22要求O-RU使用section type 8 C平面消息进行ACK/NACK反馈。关于…