用一个寓言故事讲明白KMP算法

news2024/11/16 16:40:39

在讲寓言故事之前,先讲一下理论的前提知识,避免有些0基础而无法理解

前提知识

首先KMP算法是指在串中,想要快速找出主串里跟我们的模板串一样的位置的一种算法,其主要是解决最普通的BF算法中主串指针回溯的问题。

BF算法就是一个一个比对

如果主串短还好说,BF算法看起来也不错,但是如果一旦主串长起来,要找的模板串又刚好处于主串的后面,例如主串:abcaabbabcabaacbacba      模板串:abcab,这时再按照BF算法一个一个比对,就会发现有些比对到一半后发现不一样,主串指针又只好回溯到前面去,非常浪费时间。而KMP算法的一个优点就是这个主串指针永远不回溯,要么停着不动,要么前进,没有撤退可言

那什么是KMP算法呢?

可以看到,KMP算法可以帮助你计算出模板串要右移多少位,然后又从模板串的第几位开始比对,与此同时,主串指针也仍然没有回溯。例如上图,我们比对到模板串的最后一位C时,主串指针也走到了A上,发现不一样,BF算法就会将主串指针回溯到第二位的B上,模板串往右移一位,又开始从头比对,但是KMP算法则会直接告诉你模板串要右移2位,同时,从模板串的第三位开始重新比对,这样一来,效率大大提高。

所以怎么样实现这个功能呢?

这时我们需要一个next数组(KMP算法其实在next数组上有不同的理论,例如同一个模板串ABABC,其中一个说法的next数组对应为01123,而另外一个说法的next数组对应为00120,我们这里以第一个说法为例,虽然方法不一样,但是算法思路都是一样的)

next数组的求法

首先大部分做法是:初始化j从1开始,即next[0]我们不用它,其中next[1]我们初始化为0,接下来我们就看当前位之前的模板串中最长前缀后缀重合的字符数再加一

补充(前缀和后缀):

所以我们可以推出

j=2时,B前面只有A,没有前缀和后缀,即0+1=1

j=3时,A前面有AB,前缀为A,后缀为B,没有重合的前后缀,即0+1=1

j=4时,B前面有ABA,前缀为A,AB,后缀为A,BA,有A这个重合的前后缀,即1+1=2

j=5时,C前面有ABAB,前缀为A,AB,ABA,后缀为B,AB,BAB,有AB这个重合的前后缀,即2+1=3

难点

生成next数组的函数代码

#define MAXLEN 100        //我们创建的结构体
typedef struct
{
    char ch[MAXLEN];
    int len;
}SqString;


void GetNext(SqString t,int next[])
{
    int j,k;           
    j=1;next[1]=0,k=0;     //初始化
    while(j<t.len)          //使每一个数都有对应的next[j]的值
    {
        if(k==0||t.ch[j-1]==t.ch[k-1])
        {
            j++;
            k++;
            next[j]=k;
        }
        else
        {
            k=next[k];
        }
    }

第一眼大多数都看不懂这个代码的逻辑,主要在于这几个关键的代码

一:k==0||t.ch[j-1]==t.ch[k-1]

二:next[j]=k

三:k=next[k]

这时候会很绕,我们先忘记这个代码,讲一个寓言故事,同时KMP算法我们给它换个名字:看门牌算法

寓言故事

从前有一个人,想要努力赚钱,于是他赚啊赚,很快有了一笔存款,同时他想着假如有一天钱突然全亏没了,得留一笔钱作为东山再起的资金,于是他决定,每赚钱赚到一个阶段,他就把一笔钱存进一个房间里,用神秘的门给锁上,这个神秘的门上有一个门牌号,为了避免忘记门在哪里,门上会留有上一次存钱房间的门牌号,同时打开这扇神秘门的条件是,如果未来的他还具备某一样品质,那么这扇门就可以被打开。


许多年后,他成为了一位非常有钱的人,但是在这些年中,为了赚更多的钱,他变得唯利是图,不择手段,贪得无厌,直到有一天,突然出了变故,他所有的钱全部都亏光了,他不认命,想要东山再起,但是呢,他又不想从零开始白手起家,这时他想起以前他给自己存的备用资金,

于是他找到了最近一次存的一扇门,因为是最近一次存的,所以在这扇门里留的资金是非常多的,他想要打开这扇神秘的门,发现打开这扇门的条件是要有善心,但是因为他有钱之后,从不做慈善,只愿意独自享受,所以他无法打开这扇门,但是这扇门上留有再上一次存钱的门牌号,

于是他按照这个门牌号来到了另外一个神秘的门前,但是由于这个门里是更早一段时候存的钱,所以远没有刚刚最近一次的门里存的钱多,他安慰自己至少还有一些钱,这扇门打开要求的是要有诚信,但是他之前为了赚钱,靠坑蒙拐骗赚了许多钱,早就丢失了诚信,所以这扇门他也打不开,

没办法,他只好按照这扇门挂着再上一次存的钱的门牌号去往另外一扇门,他来到这扇门,发现这扇门里存的是他第一次存钱的地方,所以没有再记录上一次的门牌号了,也就是说这是最后一扇门,因为是刚开始赚钱的时候存的,所以这里面的钱很少,他安慰自己,有总比没有好,打开这扇门的条件是要有孝心,可是他赚了钱以后,嫌弃父母是累赘,就再也没有管过父母了,所以这扇门他也打不开,

这时他认命了,发现自己当初为了赚钱,丢失了这么多宝贵的品质,于是他幡然醒悟,决定再次白手起家,东山再起。

寓言故事结束了,这时我们再回到代码中来,我们试图将代码转换为这个寓言故事,进行类比

类比

一:我们模板串比对的时候,如果发现比对错误,我们是不是不想又从头开始比对,如果告诉你我们遍历过的模板串有一个位置可以从这里开始比对,就没必要从头开始比对了

相当于故事里,这个人一直在赚钱,赚了许多钱后,突然全亏光了,他不想从0开始白手起家,如果有一扇神秘的门可以被打开,拿到里面放的备用资金,就没必要从头开始了

二:还记得我们将KMP算法改名为看门牌算法吗,是因为next[j]就相当于一个门牌的作用,如果发现比对错误,我们会找当前位置的next数组的值,然后跳回模板串等于next数组的值的j这个位置,如果这个j对应的字符等于主串指针指向的字符,我们就可以从模板串的这个位置开始比对,如果这个j对应的字符不等于主串指针指向的字符,我们再找这个位置的next数组的值,然后再跳回模板串等于next数组的值的j另外这个位置,如此循环,直到j=1时,其next[j](门牌号)为0为止

相当于故事里,这个人亏完钱后,按照门牌号的位置,如果能打开这扇门,就能让他东山再起的起点更高,这一扇门如果不行,就找下一扇门,但是越往后存的钱越少,东山再起的起点就越低,但如果找到最后一扇门都打不开,那就只能从0开始了

综上,通俗来讲上面那三行代码的意思

if(k==0||t.ch[j-1]==t.ch[k-1])就相当于故事里他要继续赚钱的条件:

第一个条件就是他亏完钱后,门全部找完了,一扇也打不开,这时再也没有门牌号了,即k==0(因为next[j]==0,按门牌号找j==0的位置不存在);

第二个条件就是他还没有亏钱,仍然在继续赚钱,越赚越多,即t.ch[j-1]==t.ch[k-1](因为没有发生比对错误,就相当于没有亏钱)

next[j]=k就相当于故事里他每赚钱赚到一定阶段,就要存钱存到一扇门里,即next[j]就相当于赚到一定阶段,=k就相当于留下门牌号

k=k[next]在else里,else就相当于故事里他发生亏钱的事情后,在一扇一扇门找的这个过程,但还没有找到最后一扇门,即k=k[next]代表按照当前找的这一扇门,给的前往下一扇门的门牌号

这时也许就能大概get到这几行代码的逻辑了吧,于是我们最后再用图解法从头理一遍就能完全明白KMP算法了(这里用的是b站的一位up视频里截图,也是他提出的看门牌的名字,寓言故事的灵感也是从这个视频来的,如果想更清晰的了解,可以去原视频里图解法里看看,非常清晰形象http://【KMP算法之求next数组代码讲解】https://www.bilibili.com/video/BV16X4y137qw?vd_source=192e499b6f41fa854f9477bb78ce75c4)

图解法

第一步,假设j==16时,他的next[j]==8,就说明了1到7这个前缀和9到15这个后缀重合,如果16等于8,那么next[17]==7+1==8,如果不等于8,这时我们按照看门牌next[16]==8,找到j==8这个位置

第二步,假设j==8时,他的next[j]==4,就说明1到3这个前缀和5到7这个后缀重合,根据对称性,我们可得另外一个子串中,9到11这个前缀和13到15这个后缀重合,也就是说这四个部分都一样

如果16等于4,那么next[17]==3+1==4,但我们主要想用到1到3和13到15这两个部分,这时我们按照看门牌next[8]==4,找到j==4这个位置

第三步,假设j==4时,他的next[j]==2,就说明1这个前缀和3这个后缀重合,根据对称性我们知道5和7也是重合的,9和11也是重合的,13和15也是重合的,即它们都是同一个字符,如果此时16==2,那么next[17]==2+1==3,如果不等于,我们推断一下就知道next[2]==1,按照看门牌就找到j==1的地方,又因为next[1]==0,所以循环就在此终结了

这时再结合图解联系刚刚类比的寓言故事,我相信大家已经能理解KMP算法了

最后理解了难点:生成next数组之后,实现KMP算法就很简单了

int IndexKMP(SqString S,SqString T)    //S是主串,T是模板串
{
    int next[MAXLEN],i=1,j=1;          //创建next数组,i是主串指针,j是门牌
    GetNext(T,next);                  //生成next数组每一项的值
    while(i<=S.len&&j<=T.len)         //如果主串指针的指向没有超出主串长度并且模板串没有比对完
    {
        if(j==0||S.ch[i-1]==T.ch[j-1])     //如本文所讲的逻辑
        {
            i++;
            j++;
        }
        else
        {
            j=next[j];
        }
    }
    if(j>T.len)        //说明模板串比对完了
    {
        return (i-T.len);        //返回主串中模板串对应的起始下标
    }
    else          //没找到
    {
        return 0;       
    }
}

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

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

相关文章

原生支付宝小程序 - 获取“dom“元素

在支付宝中是不能获取到dom元素的&#xff0c;但是我门可以借助my.createSelectorQuery来实现 Page({data:{sq:{}},onLoad(){},onShow(){this.setData({sq: my.createSelectorQuery()})} })通过axml来查看 <view>{{sq}} </view>会发现它只是一个Object&#xff0…

2024华中杯A题完整1-3问py代码+完整思路16页+后续参考论文

A题太阳能路灯光伏板朝向问题 &#xff08;完整版资料文末获取&#xff09; 第1小问&#xff1a;计算每月15日的太阳直射强度和总能量 1. 理解太阳直射辐射和光伏板的关系**&#xff1a;光伏板接收太阳辐射并转化为电能&#xff0c;直射辐射对光伏板的效率影响最大。 2. 收集…

ssm058基于Java的共享客栈管理系统+jsp

共享客栈管理系统的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对房屋出租信息管理混乱&#xff0c;出…

电脑技巧:如何把Edge浏览器扩展程序打包安装到其他浏览器

目录 1、进入浏览器扩展界面 2、找到Edge浏览器扩展插件的路径 3、找到需要扩展的插件ID 4、打开浏览器扩展插件目录 5、进入打包扩展界面 6、 安装到其他浏览器 大家日常使用浏览器的时候通常会安装很多浏览器插件&#xff0c;从而大大提升我们的办公效率&#xff0c;有…

通义灵码使用教程:探索AI编码的新维度

一、概述 介绍 通义灵码&#xff0c;是阿里云出品的一款基于通义大模型的智能编码辅助工具&#xff0c;提供行级/函数级实时续写、自然语言生成代码、单元测试生成、代码优化、注释生成、代码解释、研发智能问答、异常报错排查等能力&#xff0c;并针对阿里云的云服务使用场景…

实战:源码编译安装mariadb(10.2.19)-2024.4.18(测试成功)

目录 文章目录 目录实验环境实验软件安装相关依赖包做准备用户和数据目录准备数据库目录源码编译安装下载并解压缩源码包源码编译安装mariadb 准备环境变量生成数据库文件准备配置文件准备启动脚本,并启动服务安全初始化测试关于我最后 实验环境 centos7.6 1810 mariadb(10.2.…

CTFshow-PWN-前置基础(pwn20)

提交ctfshow{【.got表与.got.plt是否可写(可写为1&#xff0c;不可写为0)】,【.got的地址】,【.got.plt的地址】 前置基础知识&#xff1a; .got 和 .got.plt 是 ELF&#xff08;Executable and Linkable Format&#xff0c;可执行和可链接格式&#xff09;二进制文件中的两个…

OpenHarmony UI动画-rebound

简介 rebound是一个模拟弹簧动力学&#xff0c;用于驱动物理动画的库。 下载安装 ohpm install ohos/reboundOpenHarmony ohpm环境配置等更多内容&#xff0c;请参考如何安装OpenHarmony ohpm 使用说明 import rebound from ohos/rebound;功能一&#xff1a;创建维护弹簧对…

IP归属地在电商行业中的重要性

IP归属地在电商行业中的应用十分广泛且深入&#xff0c;其重要性不容忽视。它不仅是用户身份验证和地理位置识别的重要手段&#xff0c;还在个性化推荐、库存管理、物流优化以及欺诈检测等多个方面发挥着关键作用。 首先&#xff0c;IP归属地用于身份验证和安全控制。在电商交易…

2024年免费云服务器推荐,小编亲测好用!

随着云计算技术的飞速发展&#xff0c;云服务器以其弹性、高效、安全的特性&#xff0c;成为众多企业和个人用户的首选。尽管市面上有众多收费的云服务器产品&#xff0c;但免费的云服务器仍然吸引着大量用户&#xff0c;尤其是初学者和预算有限的用户。下面&#xff0c;我们就…

以太网24位应变/桥式数据采集卡,替代NI 9237

1.特性简介 XM-BRG是一款以太网型高速应变片压力桥信号采集卡&#xff0c;具有8通道(-4型为4通道)真差分输入&#xff0c;24位分辨率&#xff0c;单通道最高采样率102.4ksps八通道同步共计819.2ksps(-4型为409.6ksps)、精密前置增益放大、集成桥式传感器所需的激励源硬件支持的…

DNS服务器配置与管理(2)——BIND部署DNS

在Linux上配置DNS的常用软件是BIND&#xff08;Berkeley Internet Name Domain Service&#xff0c;BIND&#xff09;&#xff0c;它是一款实现DNS服务器的开放源码软件。本文详细介绍了在CentOS7上安装并配置Bind软件。 一、Bind软件介绍 BIND包最初是在 1980 年代初在加州大…

剑指Offer题目笔记33(并查集)

面试题116&#xff1a; 解决方案&#xff1a; ​ 一个班级可以包含一个或多个朋友圈&#xff0c;对应的图中可能包含一个或多个子图&#xff0c;每个朋友圈对应一个子图。因此&#xff0c;这个问题可以转化为如何求图中子图的数目。图的搜索算法可以用来计算图中子图的数目。扫…

3D模型处理的多进程并行【Python】

今天我们将讨论如何使用 Python 多进程来处理大量3D数据。 我将讲述一些可能在手册中找到的一般信息&#xff0c;并分享我发现的一些小技巧&#xff0c;例如将 tqdm 与多处理 imap 结合使用以及并行处理存档。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生…

PLC工业网关,实现PLC联网

在当今工业自动化领域&#xff0c;PLC&#xff08;可编程逻辑控制器&#xff09;作为控制系统的核心&#xff0c;其稳定性和可靠性至关重要。然而&#xff0c;随着工业互联网和智能制造的快速发展&#xff0c;如何实现PLC的联网通信&#xff0c;提高数据传输效率&#xff0c;成…

URL的绝对路径/相对路

一、URL 浏览器要想发起请求,必须是一个完整的url地址. URL是一个固定格式的字符串 它表达了&#xff1a; 从网络中 哪台计算机&#xff08;domain&#xff09; 中的 哪个程序&#xff08;port&#xff09; 寻找 哪个服务&#xff08;path&#xff09;&#xff0c;并注明了…

数据治理中心DataArts Studio学习

一、什么是DataArts Studio&#xff1f; 数据治理中心DataArts Studio是为了应对上述挑战&#xff0c;针对企业数字化运营诉求提供的具有数据全生命周期管理和智能数据管理能力的一站式治理运营平台&#xff0c;包含数据集成、数据开发、数据架构、数据质量监控、数据资产管理…

使用vue2-ace-editor实现可选择的代码编辑器

最近在琢磨前端&#xff0c;因项目中需要在页面上编辑代码&#xff0c;所以需要写一个代码编辑器供用户使用。找了几个编辑器相关的组件&#xff0c;对比了下感觉还是vue2-ace-editor用着舒服&#xff0c;写了demo供大家参考。 由于我的项目使用的是vue2&#xff0c;二开鹅厂的…

MySQL高负载排查方法最佳实践(15/16)

高负载排查方法 CPU占用率过高问题排查 使用mpstat查看cpu使用情况。 # mpstat 是一款 CPU 性能指标实时展示工具 # 能展示每个 CPU 核的资源视情况&#xff0c;同时还能将资源使用情况进行汇总展示 # 如果CPU0 的 %idle 已经为 0 &#xff0c;说明此核已经非常繁忙# 打印所…

算法训练营第25天回溯(分割)

回溯算法&#xff08;分割&#xff09; 131.分割回文串 力扣题目链接(opens new window) 题目 给定一个字符串 s&#xff0c;将 s 分割成一些子串&#xff0c;使每个子串都是回文串。 返回 s 所有可能的分割方案。 示例: 输入: “aab” 输出: [ [“aa”,“b”], [“a”,“…