超详细超全超好理解的KMP算法

2023-11-03

定义

KMP算法是一种字符串匹配算法,用于在一个主串中查找一个模式串的出现位置。

先看这个视频,再看下边的代码实现:

【油管阿三哥讲KMP查找算法,中英文字幕,人工翻译,简单易懂】 https://www.bilibili.com/video/BV18k4y1m7Ar/?share_source=copy_web&vd_source=4131627a2e94dad1eb4c895d415f7ad3

next[]数组求法

i 代表next数组位置指针,不回溯

j 代表回溯位置指针,如果遇到不匹配的字符,则寻找上一个字符的next值进行回溯

代码

void getNext(int* next, const string& s){
  next[0] = 0;
  int j = 0;
  for(int i=1; i<s.size(); i++){
    while(j>0 && s[i] != s[j]){
      j = next[j-1];
    }
    if(s[i] == s[j]){
      j++;
    }
    next[i] = j;
  }
}

基本逻辑:

  • 结合代码来看,比较s[i] s[j]:
    • s[i] == s[j]: j++, next[i] = j //此处为了代码简洁美观,判断完两数是否相等后,先计算j,最后在给next赋值,准确解释应该是:if s[i] == s[j]: next[i] = j +1 ; i++,j++。
    • s[i]≠s[j]: j=next[j-1] , //两数不相等的话,判断能否回溯,如果能,j回溯到j-1所对应的next值,再比较next[i]与next[j] ,如果仍旧不相等,继续回溯,直至相等或不满足回溯条件,如果不满足回溯条件,则给next赋值。
      • 回溯条件:j>0 && s[i] != s[j] // j最多回溯到0,因为回溯时 j=next[j-1] ,保证数组下标为非负,j-1 ≥ 0,则j>0.
      • s[i] ! = s[j] 回溯 j=0 仍旧不满足回溯条件,赋值next[i]=j,即next[1]=0

例子

字符串 s =“aabaabaaa” 所对应的next数组是多少?

是next[0,1,0,1,2,3,4,5,2]

在这里插入图片描述

计算步骤如下:

  • j=0,i=1, next[0] = 0; 因为第一个next值默认为0

  • i=1,j=0 s[1] == s[0] 相等 , 先j++,再赋值,j = 1,next[1] = j = 1;i++,i=2
    在这里插入图片描述

  • i=2,j=1 s[2] ! = s[1] 不等,看是否满足回溯条件 j>0 && s[i] != s[j] 满足,回溯。

    • j=next[j-1] = next[0] = 0, 再比较数组的值,s[2] ! = s[0] ,看是否满足回溯条件, j=0不满足,赋值, next[2] = j=0
    • i++,i=3

在这里插入图片描述

  • i=3,j=0 s[3] ==s[0] j++ j=1, next[3] = 1, i++,i=4
  • i=4,j=1 s[4] == s[1] j++,j=2 next[4] = 2, i++, i=5
  • i=5,j=2 s[5]==s[2] j++,j=3 next[5]=3, i++,i=6
  • i=6,j=3 s[6] == s[3] j++,j=4, next[6]=4, i++,i=7
  • i=7,j=4 s[7] == s[4],j++,j=5, next[7]=5,i++,i=8
  • i=8,j=5 s[8] ! = s[5] ,回溯,j=next[j-1]=next[4]=2,
    • s[8] ! = s[2], 回溯,j = next[j-1]=next[1] = 1,
    • s[8] == s[1] , j++,j=2, next[8]=2, i++,i=9
  • i=9 跳出循环

使用next数组进行字符串匹配

过程描述:

如下例子来描述

例子:

文本串s:a b x a b c a b c a b y

模式串t: a b c a b y
step1:求模式串的next数组

按照上述步骤求得next=0 0 0 1 2 0

step2:s与t进行匹配

定义两个下标j 指向模式串起始位置i指向文本串起始位置。如果两个字符相等,i++,j++

如果两个字符不相等,则查看前一个字符的next值,重新比较以此next值为下标的模式串的字符和文本串字符是否相等。
在这里插入图片描述

如上图所示:ab匹配,但x与c不相等,则需要查看c前面一个字符b的next值,为0,0指向a,于是比较x与a

在这里插入图片描述

如上图:x不等于a,所以i接着向前,指向a,比较a与a

在这里插入图片描述

a与a相同,i++,j++,b=b,c=c, a=a,b=b,c≠y

在这里插入图片描述

如上图:c与y不相等,则需要查看y前面一个字符b的next值,为2,2指向c,于是比较c与c

在这里插入图片描述

c=c,i++,j++,a=a,b=b,y=y

匹配完毕

代码

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

步骤重复说明一下:

在文本串s里 找是否出现过模式串t。

文本串s:a b x a b c a b c a b y

模式串t: a b c a b y

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

i不回溯

i就从0开始,遍历文本串,代码如下:

for (int i = 0; i < s.size(); i++) 

接下来就是 s[i] 与 t[j] 进行比较。

如果 s[i] 与 t[j] 不相同,j就要从next数组里寻找下一个匹配的位置。

代码如下:

while(j > 0 && s[i] != t[j]) {
    j = next[j - 1];
}

如果 s[i] 与 t[j] 相同,那么i 和 j 同时向后移动, 代码如下:

if (s[i] == t[j]) {
    j++; // i的增加在for循环里
}

如何判断在文本串s里出现了模式串t呢,如果j指向了模式串t的末尾,那么就说明模式串t完全匹配文本串s里的某个子串了。

匹配结束后,要在文本串中找出和模式串完全匹配时相匹配字符的**第一个位置 **(),所以返回当前在文本串匹配模式串的位置i 减去 模式串的长度,就是文本串字符串中出现模式串的第一个位置。

  • 文本串s:a b x a b c a b c a b y
  • 模式串t: a b c a b y
  • return的是 a b c a b y 在 a b x a b c a b c a b y完全匹配时的第一个位置a,即6
  • 完全匹配时,i指向11,j会在++一次,所以会指向数组长度6,所以 11-6+1=6
if (j == (t.size()) ) {//j指向了模式串t的末尾,j=6,此时i=s.size()-1
    return (i - t.size() + 1);
}

练习

参考:代码随想录:

首发在wolai笔记软件:
KMP算法 - https://www.wolai.com/soy7nmo4y3GDjM1HfUfUQ7

28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)

class Solution {
public:
    void getNext(int* next, const string& s){
        next[0] = 0;
        int j = 0;
        for(int i=1; i<s.size(); i++){
            while(j>0 && s[i] != s[j]){
            j = next[j-1];
            }
            if(s[i] == s[j]){
            j++;
            }
            next[i] = j;
        }
    }    
    int strStr(string haystack, string needle) {       
        if (needle.size() == 0) {
            return 0;
        }
        int next[needle.size()];
        getNext(next, needle);
        int j = 0;
        for (int i = 0; i < haystack.size(); i++) {
            while(j > 0 && haystack[i] != needle[j]) {
                j = next[j - 1];
            }
            if (haystack[i] == needle[j]) {
                j++;
            }
            if (j == needle.size() ) {
                return (i - needle.size() + 1);
            }
        }
        return -1;
    }
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

超详细超全超好理解的KMP算法 的相关文章

  • 如何使用带有进度条的 HttpClient 下载文件?

    我创建了一个名为SiteDownload并添加了一些下载图像的链接 using System Collections Generic using System Linq using System Net using System Threa
  • C++,多语言/本地化支持

    向 C 程序添加多语言支持的最佳方法是什么 如果可能 应该从包含键值对 WelcomeMessage Hello s 之类的纯文本文件中读取语言 我想到了添加一个 localizedString key 函数来返回加载的语言文件的字符串 有
  • 从数组中输入多个数字,每个数字检查是否为整数

    每个人 我希望有人能帮我弄清楚C语言的一些东西 这是我第一次认真地做IT方面的作业 我没有经验 而且我正在电子学习中学习 所以老师的帮助不是很好 我需要用C语言开发控制台应用程序 用户需要输入10个整数 如果插入的数字不是整数 需要输出错误
  • OpenGL,如何独立旋转对象?

    到目前为止我的代码 void display void glClear GL COLOR BUFFER BIT GL DEPTH BUFFER BIT Clear Screen And Depth Buffer glLoadIdentity
  • 公共领域有哪些替代方案?

    我正在用 java 编写一个游戏 正如问题标题建议的那样 我在类中使用公共字段 暂且 据我所知 公共领域很糟糕 我有一些理解其中的原因 但如果有人能澄清为什么你不应该使用它们 那将不胜感激 问题是 从我所看到的来看 这似乎是合乎逻辑的 是使
  • WCF 客户端返回空数组 - XML 响应似乎正常

    我正在尝试为我们的 Intranet 上托管的 Web 服务创建一个简单的 WCF 客户端 C 使用 Fiddler 和 SoapUI 我可以看到请求和响应似乎正常 但是当我运行代码时返回一个空数组 我会尝试只粘贴相关的行 但会是很多东西
  • dlopen 或 dlclose 未调用信号处理程序

    我在随机时间内收到分段错误 我注册了信号 但发生分段错误时未调用信号处理程序 include
  • C++ fill() 与 uninitialized_fill()

    您好 我是初学者 我想知道容器的 fill 和 uninitialized fill 之间的区别 我在谷歌上进行了快速搜索 但没有得到很好的答案 有人可以帮助我吗 fill 将值 使用赋值运算符 分配给已构造的对象 uninitialize
  • 阻止用户取消选择列表框中的项目?

    我有一个列表框 里面有很多项目 用户可以单击某个项目来编辑其内容 如何防止用户取消选择所有项目 即 用户不应该无法选择任何内容 您的情况缺少一个案例 即清除列表后 您将选择列表中不再存在的项目 我通过添加额外的检查来解决这个问题 var l
  • 从 TFS 下载工作项附件(文件已损坏)

    我正在尝试创建 C 代码 因此我可以自动从 Team Foundation Server 下载 BUGS 预定义查询的所有附件 该代码似乎工作得很好 但所有下载的文件都因意外原因而损坏 我无法查看它们 有人可以看一下代码并分享意见吗 非常感
  • 将数组显式衰减为指针

    最简洁 最惯用的方式是什么明确地将数组衰减为指针 例如 考虑您需要能够指导 SFINAE 或明确过载的情况 template
  • 我的 Opencv 应用程序处理速度非常慢

    我正在构建一个 OpenCV 应用程序 它从相机捕获视频 并在删除背景后将其覆盖在另一个视频上 我无法达到合理的速度 因为它以大约 1 fps 的速度播放输出 而我的背景去除以 3 fps 的速度工作 有没有办法以正常速度显示背景视频并以
  • 使用可变参数模板函数计算多个值的平均值

    我正在尝试编写一个函数来确定任意数量参数的平均值 所有参数都具有相同的类型 出于学习目的 我尝试使用可变参数模板函数来做到这一点 这是我到目前为止所拥有的 template
  • Lambda 按值捕获和“mutable”关键字

    关键词的必要性mutable在 lambda 中 是造成极大混乱的根源 考虑代码 int x 10 function
  • 在运行时将项目添加到 ToolStrip

    您好 我有一个带有 收藏夹 菜单的 ToolStripMenu 我想在运行时在 WinForms 应用程序中添加子项目 我有一个 datagridview 右键单击它会显示一个包含 添加到收藏夹 选项的上下文菜单 当该事件被触发时 我想使用
  • 恐怖分子已弃用

    正在接听另一个问题 https stackoverflow com q 11830514 1468366 我偶然发现了man page http linux die net man 3 herror一个名为的函数herror 看起来很像pe
  • 使用 /clr 或 clr:pure(cpprestsdk 又名 casablanca)编译时不支持互斥

    我创建一个CLR project in visual c with 64 bit配置 并尝试使用cpprestsdk aka casablanca 64bit 但是当我运行项目时 出现了错误 1 gt Build started Proje
  • 在 Visual Studio C++ 资源编辑器中导入 png 文件

    我希望能够在 Visual Studio 资源编辑器中导入 png 文件 以便能够在不同的其他项目中使用嵌入的资源 有解决办法吗 我知道它适用于位图 但我对 png 感兴趣 因为即使在较低格式 16x16 或 32x32 上也可以使用 透明
  • 致命:所有操作都需要OperationId。请为路径的“获取”操作添加它

    我正在使用 AutoRest 从 swagger json 生成 api 的客户端 输出是 AutoRest code generation utility cli version 3 0 6187 node v10 16 3 max me
  • 我应该为每个 Web 请求使用静态缓存的 ResourceManager 还是一个新实例?有关系吗?

    创建新的 NET 对性能 或其他 有何影响 如果有 ResourceManager根据每个请求new ResourceManger myResourceType FullName myResourceType Assembly 与在 Des

随机推荐

  • Python中自带的OpenCV使用指南

    Python中自带的OpenCV使用指南 OpenCV是一种广泛使用的计算机视觉库 它提供了大量的算法和工具 可以帮助用户处理图像和视频 Python中自带的OpenCV是一种基于Python语言的OpenCV库 它提供了Python开发人
  • MySQL使用UDF调用shell脚本

    前言 在最近的项目中 由于需要使用MySQL的UDF user defined function 这个特性从未使用过 而且个人觉得这个特性以后应该会经常使用 所以写下博文 记录和分享这个特性的用法 UDF是mysql的一个拓展接口 UDF
  • 通过线程+反射,解决复杂数据验证

    第一步 建立需要传入参数的 Vo 对象 ApiModelProperty value 身份证 private String idcard ApiModelProperty value 姓名 private String name publi
  • 使用linux sar命令分析CPU和磁盘

    订阅号原文 使用linux sar命令分析CPU和磁盘 一 摘要 这是夜说的第八篇学习文章 使用sar命令分析cpu和io问题 二 sar u分析cpu问题 1 sar u分析cpu问题 2代表时间间隔 s 5代表次数 这两个值可以自行调整
  • [创业-36]:《从员工到老板,你必须经历的三次跃迁》解读

    目录 前言 1 关于如何成为好的员工 第一 工资是给职位的定价 价值 第二 职位的价格由最便宜的可胜任者决定 价格 第三 解读 2 关于如何成为好的管理者 第一 衡量标准 第二 如何设计制度是衡量治理水平的一把尺 第三 解读 3 关于如何成
  • rust运行时提示link.exe找不到的问题

    直接在cmd里运行下面2句 这样就可以使用rustup来修复这个问题了 rustup toolchain install stable x86 64 pc windows gnu rustup default stable x86 64 p
  • 数据库用户管理

    数据库用户管理 一 创建 1 新建用户 CREATE USER 用户名 来源地址 IDENTIFIED BY PASSWORD 密码 用户名 指定将创建的用户名 来源地址 指定新创建的用户可在哪些主机上登录 可使用IP地址 网段 主机名的形
  • PXE服务器实现Linux全自动批量装机具体步骤

    目录 一 实验环境准备 二 CentOS7 pxe准备 三 操作步骤 1 安装dhcp tftp http syslinux 2 配置dhcp服务 3 配置tftp服务器 4 拷贝PXE服务器的相关文件到 var lib tftpboot
  • HCNP路由交换学习指南--- 静态路由

    HCNP路由交换学习指南 静态路由 文章目录 HCNP路由交换学习指南 静态路由 静态路由的基本概念 静态路由配置须知 默认路由 浮动静态路由 案例 静态路由和BFD联动 静态路由 Static Route 是网络管理员通过手工配置的方式为
  • 毕业设计-深度学习机器视觉的交通标识符识别

    目录 前言 课题背景与意义 课题实现技术思路 图像预处理 提取特征颜色 边缘提取 实现效果 最后 前言 大四是整个大学期间最忙碌的时光 一边要忙着准备考研 考公 考教资或者实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几
  • Kafka入门系列—1. topic、消费者组等重要概念

    消息队列是生产者向消息队列发送消息 消费者从消息队列拉取 pull 消息 生产者 生产者是消息队列的数据源 可以向其发送消息 如字符串 二进制数据等 消费者 消费者的数据源就是Kafka 于是通过Kafka实现了生产者和消费者两个系统的解耦
  • Anaconda的tensorflow2.0.0突然出现ERROR:root:Internal Python error in the inspect module.

    就是numpy版本的问题 直接卸载numpy版本 pip uninstall numpy 如果卸载的时候报错 把ide关掉比如Jupyter Pycharm Spyder 再下载最新版本的numpy pip install U numpy
  • js 判断数据类型

    如何判断js中的数据类型 typeof instanceof constructor prototype方法比较 如何判断js中的类型呢 先举几个例子 var a iamstring var b 222 var c 1 2 3 var d
  • php实现 密码验证合格程序(复杂问题分类,超简单的)(分类+规范编码)

    php实现 密码验证合格程序 复杂问题分类 超简单的 分类 规范编码 一 总结 一句话总结 复杂问题分类 超简单的 分类 规范编码 1 写的时候判断 不能有相同长度超2的子串重复 的时候 子串重复写成隔2位置了 应该是任意的 47 for
  • 01_手写SpringIOC思路

    文章目录 手写Spring之IOC思路整理 手写Spring之IOC思路整理 先说说老生常谈的东西 关于IOC的理解 1 Spring管理对象 IOC就是控制反转 Spring之前 我们都是自己控制对象的 new 用了Spring就是 把整
  • CSS基础语法

    CSS简介 CSS的主要使用场景就是美化网页 布局页面的 HTML的局限性 HTML只关注内容的语义 比如 h1 表明这是一个大标题 p 表明这是一个段落 img 表明这有一张图片 a 表示此处有链接 很早的时候 世界上的网站虽然很多 但是
  • 区块链技术,正面临哪些难题与挑战?

    2018年对区块链来说 是关键的一年 在这一年 区块链出现在了大众视野 成为人工智能之后的下一个科技风口 区块链新技术的来临 正在融入多个领域并催生一批创业公司 从理论上讲 区块链技术的应用范畴 可以涵盖货币 金融 经济 社会的诸多领域 但
  • emqx增加用户认证功能

    1 关闭匿名登录 首先 关闭匿名登录 编辑配置文件 emqx conf 修改为 allow anonymous改为 false 即修改后是 allow anonymous false vim emqx etc emqx conf 操作演示
  • Windows下使用zsh——WSL(Debian)方法

    转载自我的个人博客 建议直接跳转个人博客查看 这个复制过来居然没有图 陈狗说windows下命令行太难用可以换成zsh 根据网上教程 GPT4的提示搞着玩 记录一下过程 我使用了WSL zsh的方法 也可以使用Git Bash zsh 1
  • 超详细超全超好理解的KMP算法

    定义 KMP算法是一种字符串匹配算法 用于在一个主串中查找一个模式串的出现位置 先看这个视频 再看下边的代码实现 油管阿三哥讲KMP查找算法 中英文字幕 人工翻译 简单易懂 https www bilibili com video BV18