字符串匹配 --- BF算法 KMP算法

 Welcome to 9ilk's Code World

       

(๑•́ ₃ •̀๑) 个人主页:       9ilk

(๑•́ ₃ •̀๑) 文章专栏:    算法Journey


本篇博客我们将介绍关于字符串匹配的BF算法以及KMP算法,请放心食用~


🏠 字符串匹配

假设有一个字符串为主串str,另一个字符串为子串sub,我们需要在str中找到sub出现的第一个位置,没有就返回-1表示找不到。

eg :  str = “abcde”   sub = “bcd”   ---> 子串在主串第一次出现的位置是1

🏠 BF算法

    BF算法也叫做“暴力匹配算法”,也就是在主串固定一个左端点left,定义一个右端点right,right一开始在left位置,让right遍历一次主串,子串也用一个变量cur遍历,没找到匹配的让left向前移动一位,cur回退到子串起始位置,right回退到新left位置再次遍历,直到left找到匹配的子串或者找不到的情况。

动画展示:

注意点:

  • 当子串为空串时没必要此时表示没找到
  • 当子串大于主串时无法进行匹配
  • 当开始匹配的位置不合理时无法进行匹配
  • 结束条件 : cur走完了子串找到了 / left走完了主串但cur没走完表示没走到

参考代码:

//BF算法
int BF(string & str, string& sub,int pos = 0)
{//判断位置合法性 以及主串子串合法性int lenstr = str.size();int lensub = sub.size();if (lensub > lenstr || lensub == 0)return -1;if (pos < 0 || pos >= lenstr)return -1;int left = pos; int right = left; //遍历主串int cur = 0;while (cur < lensub && left < lenstr){if (str[right] == sub[cur]){cur++;right++;}else{cur = 0;left++;right = left;}}//终止条件if (cur >= lensub){return left;}return -1;
}

时间复杂度 : O(m*n) m为主串长度,n为子串长度


🏠 KMP算法

    📒 引入

在了解完BF算法后,对于此时的不匹配,我们很自然的会想要把left向前挪,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把"搜索位置"移到已经比较过的位置,重比一遍。

那有什么改进效率的方法呢?

📒 什么是KMP

对于此时不匹配我们能提取到的一个信息是你其实知道前面六个字符是"ABCDAB",因此我们可以利用这个信息找到最大程度能匹配子串的一部分,此时就是最好的情况

这便是KMP算法的核心 ---  利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。与BF算法不同的是,当匹配失败时,主串时不回退的,子串回退到相应位置。

问题是KMP算法中,当匹配失败时,子串怎么知道该回退到哪个位置?

📒 next数组

所谓next数组,保存的是子串中某个位置匹配失败后应该回退到的位置

  • 规律1

对于这对字符串匹配,当在i位置匹配失败时,为了能够在主串中尽量找到和子串匹配的一部分,此时j最好回退到2号下标,而我们发现子串中[0,1]和[3,4]这两个相同的部分长度正好是2

因此找j所要回退的位置关键是去找在主串匹配成功的部分里面取去找到一部分子串1(比如上面子串的【3,4】)和我们子串里面一部分串是相同的(比如子串里面的【0,1】和【3,4】)

✏️ 手动求next数组

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

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

示例1 :

对于下标7的位置,它前面对应的【0,1】和【5,6】区间是两个相等的真子串,因此当在7下标匹配失败时,j应该回退到2号位置,也就是next[7] = 2;

示例2 :

对于下标10的位置,它前面【0,6】和【3,9】区间是两个相等的真子串,因此此时next[10]  = 7;

练习:

  • 对于"ababcabcdabcde",求其的next数组?
  • 再对"abcabcabcabcdabcde",求其的next数组?"

答案 :

1. -10012012001200

2. -100012345678901230

小tips : 每个next数组中next[i+1]如果是大于next[i],一定有next[i + 1] = next [i] + 1; 

✏️ 已知next[i] 如何求 next[i+1] 

前提 : 假设 next [ i ] = k;

  • p[i] = p[k]时(比如下面字符串中下标8对应字符等于下标3对应字符)

我们假设x为第二个真子串起始位置,注意x的位置不一定就是k

因此我们可以得到 k - 1 - 0 = i - 1 - x;

--> x = i - k;  k = i - x;

--->p[0]...p[x] == p[0]...p[k-1] == p[i-k]...p[i-1] (1)

又由于 p[i] == p[k]  (2)

由(1)(2)式得 : p[0]...p[k-1][k] == p[i-k]...p[i-1]p[i] 

---> p[0]...p[k] == p[i-k]...p[i]   (3)

由数学归纳法可知: next[i] = k 对应 (1)式,同理next[i+1] = k + 1对应(3)式

  • p[i] != p[k]

我们可以发现此时next[i+1] != k+1 ,也就是k回退到的2号位置不是你需要的,那怎么办呢?此时我们需要继续回退到2号对应next数组的值,此时k回退到了0下标且此时p[i] = p[k],k+1正好等于next[i+1],即next[6]

因此 如果p[i] != p[k] ,此时我们需要一直回退,回退到满足p[i] == p[k]的情况.

📒 简单实现KMP算法

  • 获得next数组

整体思路是基于我们推导的next[i] 求 next[i+1]分为p[i] == p[k] 和 p[i] != p[k]两种情况,由于下标0和1我们已知(规定了是-1和0,next[0] = -1,next[1] = 0),因此我们在求next[2]时我们可以利用next[1],同理next[3]可以利用next[2]... 因此我们next[i]的求法就是去利用next[i-1].

注: 当p[i] != p[k]时,我们的k可能一直回退到-1此时我们把他归类到p[i] == p[k]的情况直接让此时的next[i] = k + 1;

参考代码 : 

void GetNext(vector<int>& next,string& sub)
{if (sub.size() >= 1){next[0] = -1;}next[1] = 0;int k = 0;//指的是前一项的kint i = 2; //从下标为2开始while (i < sub.size()){if (k == -1 || (sub[i - 1] == sub[k])) // p [i] == p [k]{next[i] = k + 1;i++;k++;}else{k = next[k]; // p[i] != p[k] 回退到满足 p[i] == p[k]}}}
  • KMP算法整体逻辑

核心步骤:

1. 获取next数组

2. 匹配失败时,根据next数组的信息快速匹配

⚠️:主串和子串和合法性以及开始匹配位置的合法性

参考代码:

int KMP(string& str, string& sub, int pos = 0)
{int lenstr = str.size();int lensub = sub.size();if (pos < 0 || pos >= lenstr)return -1;if (lensub > lenstr || lensub == 0)return -1;int i = pos; //遍历主串int j = 0; // 遍历子串//获取next数组vector<int> next;next.resize(lensub);GetNext(next,sub);while (i < lenstr && j < lensub){if (j == -1 || str[i] == sub[j]) // 注意j可能根据匹配信息回退到-1{i++;j++;}else{j = next[j]; //匹配失败要回退的位置}}if (j >= lensub)return i - j;elsereturn -1;}

本博客主要是简单的科普字符串算法,文章设计的数学知识可能不严谨欢迎指正!

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

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

相关文章

2.移植freertos到stm32f103c8t6

目录 1.步骤 2.freertos配置时常见的选项卡意思 1.步骤 2.freertos配置时常见的选项卡意思

【unity实战】Unity中基于瓦片的网格库存系统——类似《逃离塔科夫》的库存系统

最终效果 文章目录 最终效果前言素材下载图片配置获取格子坐标动态控制背包大小添加物品移动物品物品跟随鼠标创建物品的容器&#xff0c;定义不同物品修改物品尺寸修复物品放置位置问题按物品尺寸占用对应大小的格子判断物品是否超出边界范围物品放置重叠&#xff0c;交换物品…

深度解析:机器学习如何助力GPT-5实现语言理解的飞跃

文章目录 文章前言机器学习在GPT-5中的具体应用模型训练与优化机器翻译与跨语言交流&#xff1a;情感分析与问答系统&#xff1a;集成机器学习功能&#xff1a;文本生成语言理解任务适应 机器学习对GPT-5性能的影响存在的挑战及解决方案技术细节与示例 文章前言 GPT-5是OpenAI公…

24/06/26(1.1129)动态内存

strtok 字符串分割函数 #include<stdio.h> int main(){ char str[] "this,a sample string."; char* sep ","; char* pch strtok(str, sep); printf("%s\n", pch); while (pch ! NULL){ printf("%s\…

惊天大瓜姬圈天莱女明星出轨风波

#惊天大瓜&#xff01;姬圈天菜女明星出轨风波#近日&#xff0c;娱乐圈掀起了一场前所未有的风暴&#xff01;狗仔队放出重磅消息&#xff0c;直指某位姬圈天菜级别的女明星深陷出轨泥潭。消息一出&#xff0c;引发了网友们的热议和猜测&#xff0c;究竟这位神秘的女明星是谁&a…

各式各样神奇的注意力机制变型

当输入sequence N很大的时候&#xff0c;这个时候self-attention 占据了绝大部分算力 Local Attention / Truncated Attention 只需要关注附近的attention, local attention 和cnn 差不多&#xff0c;可以加速计算&#xff0c;但效果不一定好 Stride attention 空几格attenti…

[保姆级教程]uniapp自定义标签页切换组件

文章目录 导文样式改成动态列表切换点击效果加上点击自动滑动scroll-view加上切换组件效果 导文 unaipp自带的标签页和ui设计相差太大&#xff0c;直接修改组件比手写一个还麻烦&#xff0c;下面手写一个。 样式 先用scroll-view做一个滑动&#xff0c;不然多的话滑动不了。 &l…

神经网络学习8-反向传播

back propagation 拿到前面传回来的L对z的偏导&#xff0c;再分别算损失值对x和w的偏导 反向传播 前馈过程求局部梯度 反向传播 这里的loss&#xff08;wxb-y)^2,第一个关于b的偏导为2(wxb-y),第二个关于w的为2w(wxb-y)

【机器学习300问】128、简述什么Word2Vec?

一、一句话说明Word2Vec是什么&#xff1f; Word2Vec是一种常见的词嵌入技术。Word2Vec的目标是将每个词表示为一个向量&#xff0c;使得这些向量能够反映出词语之间的相似性和关联性。 word2vec算法通过预测中心词和上下文词的共现概率来学习词向量&#xff0c;能够捕捉词语之…

植物大战僵尸杂交版v2.1最新直装版,苹果+安卓+PC+防闪退工具+修改工具+高清工具+通关存档整合包更新

今天我要和各位聊聊一款让全网疯狂的游戏——《植物大战僵尸杂交版》。这可不是简单的游戏&#xff0c;它可是让B站的UP主“潜艇伟伟迷”一夜成名的大作&#xff0c;让无数玩家为之疯狂的魔改神作&#xff01; 记得2009年&#xff0c;《植物大战僵尸》横空出世&#xff0c;那时…

LDO芯片手册,实例应用分析

在进行电路设计时LDO是经常用到的&#xff0c;尤其在为芯片&#xff0c;晶振等敏感电路进行供电时应用更多&#xff0c;下面选取一款比较常用的LDO芯片&#xff0c;一起进行更深入的学习。 SGM2036特点简介 SGM2036&#xff0c;圣邦微一款比较常用的LDO芯片手册 可以先大致看…

【ajax实战04】数据管理平台——富文本编辑器

一&#xff1a;富文本编辑器简介 富文本&#xff1a;带样式&#xff0c;多格式的文本&#xff0c;在前端一般使用标签配合内联样式实现。 富文本编辑器&#xff1a;用于编写富文本内容的容器 二&#xff1a;wangEditor插件 https://www.wangeditor.com/ 对于将富文本编辑器…

CAN通信协议

文章目录 STM32-CAN1. CAN基础知识2. CAN协议2.1. CAN协议与ISO/OSI基本参照模型的关系2.2. CAN协议及标准规格2.3. CAN协议2.3.1. 帧的种类2.3.2. 数据帧2.3.3. 遥控帧2.3.4. 错误帧2.3.5. 过载帧2.3.6. 间隔帧 2.4. 优先级的决定2.5. 位填充2.6. 错误的种类2.7. 位时序 3. CA…

鸿蒙系统最简单安装谷歌服务及软件的方法

哈喽&#xff0c;各位小伙伴们好&#xff0c;我是给大家带来各类黑科技与前沿资讯的小武。 近日&#xff0c;华为开发者大会在东莞松山湖召开&#xff0c;发布了盘古大模型5.0和纯血版的鸿蒙 HarmonyOS NEXT 全场景智能操作系统&#xff0c;而根据研究机构 Counterpoint Resea…

ITSG、COST-G、Tongji和WHU Level-2数据产品读取绘图(Matlab)

数据介绍&#xff1a; ICGEM International Center for Global Gravity Field Models (gfz-potsdam.de) ITSG 2018&#xff1a;Institute of Geodesy at Graz University of Technolog&#xff08;格拉茨理工大学大地测量研究所&#xff09; 2018版本&#xff0c;最高60阶球谐…

Java导出excel合并行功能

导出的excel需要上下行相同的数据进行行合并的功能。如图显示 这里我使用的是项目框架自带的导出模板代码&#xff0c;是在这套模板基础之上做的修改。 // 我主要演示的就是mergeRows方法的操作&#xff0c;dataList是导出数据的集合。 workbook ExcelTools.expData(workbook…

大厂面试官问我:Redis中热key和大key是怎么解决的?【后端八股文五:Redis热key和大key八股文合集】

往期内容&#xff1a; 大厂面试官问我&#xff1a;Redis处理点赞&#xff0c;如果瞬时涌入大量用户点赞&#xff08;千万级&#xff09;&#xff0c;应当如何进行处理&#xff1f;【后端八股文一&#xff1a;Redis点赞八股文合集】-CSDN博客 大厂面试官问我&#xff1a;布隆过滤…

振兴黄河新生力 打造文旅新地标——全国首家黄河会客厅在山东济南启幕

6月26日&#xff0c;由黄河文化发展工作站组织实施的全国首家黄河会客厅平台发布会暨山东基地启动仪式在济南成功召开。黄河会客厅以“民生黄河、生态动能、中华文明”为核心主题&#xff0c;融汇黄河智库、黄河文明、黄河产域、黄河金融、黄河科创、黄河物贸六大振兴赋能体系&…

计算机视觉:项目实战

目录 SSD1.安装ananconda2.安装cuda和cudnn3.配置Pytorch环境3.1 pytorch环境的配置与激活3.2 pytorch库的安装3.3 其它依赖库的安装 遇到的问题&#xff1a;1.EOFError: Ran out of input.2.No module named dlib. SSD 1.安装ananconda 见另一篇博文&#xff1a;https://blo…

OpenAI API一键搬家,天工推出开发者迁移计划

6月25日&#xff0c;OpenAI宣布称将于今年7月9日开始封锁来自非支持国家和地区的API流量。此后&#xff0c;来自中国大陆、中国香港等地的开发者将无法使用OpenAI API提供服务。 为了助力开发者高效切换至国内大模型&#xff0c;天工开放平台&#xff08;https://model-platfo…