KMP算法

KMP算法

为什么叫做KMP呢。

因为是由这三位学者发明的:Knuth,Morris和Pratt,所以取了三位学者名字的首字母。所以叫做KMP

next数组就是一个前缀表(prefix table)。

前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。

要在文本串:aabaabaafa 中查找是否出现过一个模式串:aabaaf。

前缀表是如何记录的呢?

首先要知道前缀表的任务是当前位置匹配失败,找到之前已经匹配上的位置,再重新匹配,此也意味着在某个字符失配时,前缀表会告诉你下一步匹配中,模式串应该跳到哪个位置

前缀表

前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。

后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串

aabaaf

前缀

a
aa
aab
aabaa
aabaaf  不是

后缀

f
af
aaf
baaf
abaaf
aabaaf 不是

求最长相等前后缀长度

a          0
aa         1
aab        0
aaba       1
aabaa      2
aabaaf     0

得出前缀表

aabaaf
010120

**为什么最长相等前后缀,开始,就能继续匹配其他的?**
因为如果我算出了前缀和后缀是相等的话,那么我第一次在匹配前缀的时候,其实已经完成了后缀的部分匹配工作。 举个例子:原串aabaabaaf,模式串aabaaf 一开始从第一个a匹配到第二个a的同时,实际上已经完成了从第四个a到第五个a的匹配过程,但是必须知道是模式串的前后缀部分相同,模式串第二个aa的匹配就可以省去了,就可以直接让原串的第二个b和模式串的b匹配成功了

next数组

放的也是前缀表,会对前缀表调整

next数组实际上可以自减一或者前移一处理,不同的编码习惯导致的

 a  a  b  a  a  f0  1  0  1  2  0
-1  0  1  0  1  2   //右移     //遇见冲突,就找冲突的位置所对应的下标
-1  0 -1  0  1 -1   //整体减一 //遇见冲突,就找冲突的前一位所对应的下标加1

1.前后缀相等
2.前后缀不同
3.更新

void getNext(int* next, const string& s){int j = -1;next[0] = j;for(int i = 1; i < s.size(); i++) { // 注意i从1开始while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了j = next[j]; // 向前回退}if (s[i] == s[j + 1]) { // 找到相同的前后缀j++;}next[i] = j; // 将j(前缀的长度)赋给next[i]}
}

KMP算法的前缀next数组最通俗的解释,如果看不懂我也没辙了_next数组是针对-CSDN博客

a a a b(索引 0~3)​

字符索引0123
字符aaaa
next-1012
  1. 初始化

    • next[0] = -1(第一个字符无前缀和后缀)
  2. 计算 next[1](字符 a,索引 1)​

    • 前一个字符的 next 值为 next[0] = -1,比较 P[1]P[-1 + 1] = P[0](即第一个 a)。
    • P[1] = aP[0] = a相等next[1] = next[0] + 1 = -1 + 1 = 0
  3. 计算 next[2](字符 a,索引 2)​

    • 前一个字符的 next 值为 next[1] = 0,比较 P[2]P[j + 1] = P[1](即第二个 a)。
    • 前面的是1,**说明前面的字符已经和第一个相等了
    • P[2] = aP[1] = a相等next[2] = next[1] + 1 = 0 + 1 = 1
    • 对称性累加了
  4. 计算 next[3](第四个 a,索引 3)​

    • 前一个字符的 next 值为 next[2] = 1,比较 P[3]P[1 + 1] = P[2](第三个 a)。
    • 匹配成功next[3] = next[2] + 1 = 1 + 1 = 2
字符索引0123
字符aaab
next-1010
  1. 计算 next[3](字符 b,索引 3)​

    • 前一个字符的 next 值为 next[2] = 1,比较 P[3]P[1 + 1] = P[2](即第三个 a)。
    • P[3] = bP[2] = a不相等,需要回溯:
      • 回溯到 j = next[1] = 0,比较 P[3]P[0 + 1] = P[1] = a → 仍不相等。
      • 继续回溯到 j = next[0] = -1,比较 P[3]P[-1 + 1] = P[0] = a → 仍不相等。
      • 最终 j = -1next[3] = -1 + 1 = 0
        —>

a. 求得是最长相等前后缀长度

 如果找到了相同的前后缀   当前面字符串(不包括当前字符)的最长前后缀长度下标的字符 与 当前的字符相等时结果为  j++ , next[i]=j(匹配的字符个数加1);如 a   a   a  a     计算 next[3](第四个 a时),前面的最长前后缀长度为2 前缀和后缀为(aa)若要继承下它的对称性 那么后缀为(aa a) ,前缀也要为(aa a) 即 当前字符(s[i]) 要等于 最长前后缀长度下标的字符(s[j+i]) a a (a)  a  与  a a a (a)(由于我使用了减一操作 所以为 j+1 , 最后若是相等了说明最长前后缀长度+1, 若索引从1开始算也就是最长前后缀长度的下一位下标字符)为何可以继承对称性    前缀有 a0 aa01 aaa012      注意要求的是最长相等前后缀长度  不是相等的前后缀个数后缀有 a3 aa23 aaa123      那么前面的最长前后缀长度为正数 说明  (aa) a a  要可以与 a (aa) a 匹配 那么只要(aaa)a和a(aaa)即可继承对称性故只要相同的前后缀 即可继承对称性 (j+1)

b. 当前面字符的前一个字符的对称程度为0的时候,说明前一个字符与第一个字符相同, 同对称程度为n时,前一个字符 到该字符前n位a (aa) a 与 第一位字符到第一位字符后n位(aa) a a相同
d. 那么只要匹配第一位字符的后n位的下一位字符是否与当前字符相同,那么就可以对称性+1
e. j记录的是前缀和后缀的长度 也就是 s[ j ] 为 相同的前后缀 的前缀最后一个字符

c. 当前后缀不相同了 (通过递推逻辑保证前后缀的匹配性)

若是前后缀不相同了 需要不断向前回退 回退的目的就是找到可以继承的对称性 也就是找到相同的前后缀

 `next[j]` 的定义是:​**子串 `s[0..j-1]` 的最长相等真前缀和后缀的长度**。  

(aa) a aa a (aaa) b 不对
回溯到 j = next[j] = 0 就是返回到当前已匹配更短的前缀 (aa) a a b 与 a a (aa) b ,比较 P[3] 和 `P[0 + 1] = P[1] a(a) aa aa a(b)
由于next[j] 说明 没有b时 aa a b 有相等前后缀 ,那么表明是前缀s[0~j] 与 后缀s[(i - j)~ (i-1)]

已知

为何可以回退再找相同的前后缀
前缀是从前到后的 都是从左到右顺序
后缀是从后到前的 那么只要存在前面的最长前后缀长度为正数,就说明存在前缀和的后缀(不包括当前字符下的后缀)可以匹配 (a )a a b 与 a a (a) b
然后 (a a) aa b 与 a a (aa b) s[i] != s[j + 1] 确定是否要接着回退 , 若找不到就是不存在 , j 为-1 (会返回到next[0])

当i=3时,next[3]=2确保s[0…1]=s[1…2],当i=4时,回退到j=1,前缀s[0…1]必须与s[2…3]匹配,因为此时处理的是更长的子串,而后缀的起始位置由i和j共同决定。

  • 为什么 s[0..1] 必须与 s[2..3] 匹配?
    • next[3] = 2 已保证 s[0..1]s[1..2] 匹配。。
    • 当处理前 4 个字符 "aaaa" 时,后缀范围从 s[1..2] 扩展为 s[2..3](因为子串长度从 3 变为 4)。
    • 由于前 3 个字符的后缀 s[1..2] 已匹配前缀 s[0..1]
      关键:由于 s[3] 的值与 s[1] 相同(均为 'a'),因此 s[2..3] = "aa" 必然与 s[0..1] = "aa" 匹配。

呃呃呃呃呃 ,感觉还是有点模糊,s[0..1]s[2..3] 匹配

使用next数组来做匹配

在这里插入图片描述

在这里插入图片描述
如果我们在后缀的后面不匹配了,就在与其相等的前缀的后面继续开始匹配

012345
aabaaf
010120前缀的后面的下标正好是前缀的长度
也就是从b开始重新匹配

定义两个下标j 指向模式串起始位置,i指向文本串起始位置。

int j = -1; // 因为next数组里记录的起始位置为-1
for (int i = 0; i < s.size(); i++) { // 注意i就从0开始while(j >= 0 && s[i] != t[j + 1]) { // 不匹配j = next[j]; // j 寻找之前匹配的位置}if (s[i] == t[j + 1]) { // 匹配,j和i同时向后移动j++; // i的增加在for循环里}if (j == (t.size() - 1) ) { // 文本串s里出现了模式串treturn (i - t.size() + 1);}
}

代码

class Solution {
public:vector<int> get_Next(string needle){vector<int> next(needle.size(),0);next[0]=-1;int j=-1;for(int i=1;i<needle.size();i++){while(needle[i]!=needle[j+1]&&j>=0){j=next[j];}if(needle[i]==needle[j+1]){j++;}next[i]=j;}return next;}int strStr(string haystack, string needle) {if(needle.size()==0)return 0;vector<int> next = get_Next(needle);int j=-1;for(int i = 0;i<haystack.size();i++){while(j >= 0 && haystack[i] !=needle[j + 1]) j=next[j];if(needle[j+1]==haystack[i])j++; if(j==needle.size()-1) return (i - needle.size()+1);}return -1;}};

力扣题目链接 https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/

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

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

相关文章

3D点云数据处理中的聚类算法总结

1.欧式聚类&#xff1a; 基于点的空间距离&#xff08;欧几里得距离&#xff09;来分割点云&#xff0c;将距离较近的点归为同一簇。 欧式聚类需要的参数&#xff1a;邻域半径R,簇的最小点阈值minPts&#xff0c;最大点数阈值maxPts。 实现效率&#xff1a; O(n * log n) 实现…

WRC世界机器人大会-2024年展商汇总

2024世界机器人大会 时间&#xff1a;2024年8月21日至25日 地点&#xff1a;北京经济技术开发区北人亦创国际会展中心 大会主题&#xff1a;共育新质生产力&#xff0c;共享智能新未来 2024世界机器人博览会亮点纷呈&#xff0c;20余款人形机器人整机将亮相博览会&#xff…

拉取镜像,推送到阿里云镜像仓库

需求背景&#xff1a;在学习k8s&#xff0c;虚拟机无法正常拉取 wangyanglinux/tools:busybox 镜像。 解决办法&#xff1a;将墙外镜像拉到国内&#xff08;阿里云&#xff09;再使用 准备工作需要创建对应的镜像仓库&#xff0c;然后再进行推送 1. 拉取镜像 docker pull …

DeepSeek和Kimi在Neo4j中的表现

以下是2个最近爆火的人工智能工具&#xff0c; DeepSeek:DeepSeek Kimi: Kimi - 会推理解析&#xff0c;能深度思考的AI助手 1、提示词&#xff1a; 你能帮我生成一个知识图谱吗&#xff0c;等一下我会给你一篇文章&#xff0c;帮我从内容中提取关键要素&#xff0c;然后以N…

哈尔滨工业大学DeepSeek公开课人工智能:大模型原理 技术与应用-从GPT到DeepSeek|附视频下载方法

导 读INTRODUCTION 今天继续哈尔滨工业大学车万翔教授带来了一场主题为“DeepSeek 技术前沿与应用”的报告。 本报告深入探讨了大语言模型在自然语言处理&#xff08;NLP&#xff09;领域的核心地位及其发展历程&#xff0c;从基础概念出发&#xff0c;延伸至语言模型在机器翻…

redis解决缓存穿透/击穿/雪崩

文章目录 1.缓存穿透1.1 概念1.2 解决方案1.2.1 缓存空对象1.2.2 布隆过滤 1.2 店铺查询使用缓存穿透解决方案1.2.1 流程 2.缓存雪崩2.1 什么是缓存雪崩&#xff1f;2.2 雪崩解决方案 3.缓存击穿3.1 什么是缓存击穿&#xff1f;3.2解决方案3.2.1 基于互斥锁解决缓存击穿问题&am…

不连续平面提取

不连续平面提取 提取流程 #mermaid-svg-Y87uP8WsVRmPYriG {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Y87uP8WsVRmPYriG .error-icon{fill:#552222;}#mermaid-svg-Y87uP8WsVRmPYriG .error-text{fill:#552222;s…

大语言模型-2.2/3-主流模型架构与新型架构

简介 本博客内容是《大语言模型》一书的读书笔记&#xff0c;该书是中国人民大学高瓴人工智能学院赵鑫教授团队出品&#xff0c;覆盖大语言模型训练与使用的全流程&#xff0c;从预训练到微调与对齐&#xff0c;从使用技术到评测应用&#xff0c;帮助学员全面掌握大语言模型的…

数据库操作练习

一.向heros表中新增一列信息&#xff0c;添加一些约束&#xff0c;并尝试查询一些信息 //向表中添加一列age信息 alter table heros add column age int;//id列添加主键约束&#xff0c;设置自增 alter table heros modify column id int auto_increment primary key;//name列…

CTF【WEB】学习笔记1号刊

Kali的小工具箱 curl www.xxx.com&#xff1a;查看服务器响应返回的信息 curl -I www.xxx.com:查看响应的文件头 一、cmd执行命令 ipconfig&#xff1a;ip地址配置等&#xff1b; 二、 Kali操作 1.sudo su&#xff1b; 2.msfconsole 3.search ms17_010 永恒之蓝&#xff…

在 SaaS 应用上构建 BI 能力的实战之路

SaaS 产品在持续运营过程中积累了大量数据&#xff0c;这些数据不仅是数字的记录&#xff0c;更是洞察市场趋势、优化产品功能、提升用户体验的宝贵资源。 因此&#xff0c;大部分的 SaaS 产品在发展到一定阶段后&#xff0c;都会开始构建自己的报表模块或分析模块&#xff0c;…

gonet开源游戏服务器环境配置

1.mysql搭建 搜索mysql-server apt安装包名 sudo apt search mysql-server 安装mysql-server sudo apt-get install mysql-server 安装完成后会&#xff0c;启动mysql服务及创建系统服务 查看服务状态 systemctl status mysql.service 使用超级权限登陆mysql sudo mysql 授…

STM32基础篇(五)------TIM定时器比较输出

简介 定时器的类型 在《STM32F10xxx参考手册&#xff08;中文&#xff09;.pdf》中可以看到下面三个章节 因此可以得到 高级定时器含有通用定时器的所有功能&#xff0c;通用定时器含有基本定时器的所有功能&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;…

基于STM32的两路电压测量仿真设计Proteus仿真+程序设计+设计报告+讲解视频

基于STM32两路电压测量仿真设计(Proteus仿真程序设计设计报告讲解视频&#xff09; 仿真图Proteus 8.9 程序编译器&#xff1a;keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;C0106 1.主要功能 基于STM32单片机设计一个双路电压检测器 1.系统可以测量两路输入电…

210、【图论】课程表(Python)

题目 思路 这道题本质上是一个拓扑排序。每次先统计每个点的入度个数、然后再统计点与点之间的邻接关系&#xff0c;找到入度为0的点作为起始遍历点。之后每遍历到这个点之后&#xff0c;就把这个点后续的邻接关系边的点入度减去一。当某个点入度为0时&#xff0c;继续被加入其…

react 杂记2 优化hook

useEffect 每个Fiber节点都会为该组件的所有effec对象​维护一个链表, 场景​类组件方法函数组件等效写法差异说明挂载时执行componentDidMount()useEffect(fn, [])useEffect 副作用在浏览器绘制后异步执行&#xff1b;componentDidMount 是同步的。更新时执行componentDidUp…

Java内存泄漏、CPU飙升排查

在Java应用开发中&#xff0c;内存泄漏和CPU飙升是两类高频出现的生产问题&#xff0c;也是常见的面试问题。这里通过一些demo进行实践。 内存泄漏 private static List<byte[]> leakList new ArrayList<>();GetMapping("/memory/leak") public void …

【搜索】dfs(回溯、剪枝、记忆化)

个人主页&#xff1a;Guiat 归属专栏&#xff1a;我讲你听 文章目录 1. dfs 回溯1.1 回溯介绍1.2 回溯模板1.3 回溯经典题目 2. dfs 剪枝2.1 剪枝介绍2. 2 剪枝模板2.3 经典题目 3. dfs 记忆化3.1 记忆化介绍3.2 记忆化示例 正文 1. dfs 回溯 1.1 回溯介绍 核心思想&#xff…

emWin自定义键盘布局

emWin V6.46提供了自带的键盘控件&#xff0c;用起来功能还是比较齐全的。但是有些时候自带的布局不能满足要求&#xff0c;此时可用键盘的结构体来自定义布局。 KEYDEF_KEYBOARD MyNumPad;static KEYDEF_AREA NumPadKeyArea[4] {{10, 0, 720, 250}, //每行按钮的坐标和占用…

人工智能之数学基础:瑞利商与特征值的关系

本文重点 瑞利商是线性代数中的一个重要概念,具有丰富的性质和广泛的应用。通过求解瑞利商的最大值或最小值,可以找到矩阵的特征值和特征向量,进而解决降维、聚类、优化和计算机视觉等领域的问题。广义瑞利商作为瑞利商的推广形式,在机器学习和数据分析中也发挥着重要作用…