字符串匹配——烦人的KMP

        相信很多同学看到这篇文章的时候,已经被KMP拿捏了吧!KMP算法说难,倒也不是很难,手算都会,说不难吧,短短几行代码愣是看不懂,辗转反侧,翻书查阅,视频讲解,最后还是一头雾水,百思不得其解(俺也一样.jpg),去年准备初试的时候,学到这里觉得这个算法很神奇,就想搞懂它,但是想了好几天都不太理解,考试也不考代码,就暂且搁置了,奈何复试要上机,故打开力扣刷刷题,开篇就看到KMP,又唤起了我那该死的记忆,昨晚冥思苦想一个半小时,终于悟出正道,来跟大家分享分享……(好久没更博客了,有点话痨)

        还不会手动模拟的同学,可以先去学习一下它的原理,我觉得王道咸鱼学长这个讲得不错4.2_2_KMP算法(旧版上)_哔哩哔哩_bilibili。接下来,完成这道题,就一起反向拿捏这个烦人的KMP吧!


转自力扣

找出字符串中第一个匹配项的下标

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回  -1 

示例 1:

输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。

示例 2:

输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。
提示:1 <= haystack.length, needle.length <= 104haystack和 needle仅由小写英文字符组成​​​​​

理解next数组:

先举个例子理解一下:模式串为ababaa,手算可得出其next数组为:011234,next数组执行如下:

从图中可发现,每次回溯,前面都是有相同匹配的子串,本身匹不匹配不重要(我之前理解是,本身也要匹配,例如,第四位的b回溯到第二位的b,第四位的b前面是aba,第二位的b前面是a,有重复的子串a;第六位的a回溯到第四位的b,第六位的a前面是ababa,第四位的b前面是aba,有重复的子串aba,但a和b并不相等;再来看代码,

void getNext(int next[], string s)
{
    next[0]=-1;
    int i=0, j=-1;
    while(i<s.length())
    {
        if(j==-1||s[i]==s[j])//前面都匹配
        {
            i++;
            j++;
            next[i]=j;
        }
        else
        {
            j=next[j];
        }
    }
}

大家可以想象一下这个场景,拿两个相同的串进行匹配,一前一后,相差一个位置,如果相同,说明到目前为止我都是跟你相同的,而i++、j++,使得 i 指针和 j 指针之前都是相同的(准确的来说是有相同子串),则next[ i ] = j,表示当 i 不匹配的时候,可以跳到 j。如果这两个指针所指的不相等,说明 i 指针和 j 指针之前也是相同的,但是 i 指针和 j 指针失配了,只能让 j = next[ j ],让 j 回溯,j 去找跟它前面相同的,让 i 指针重新和新的 j 指针匹对;重复以上工作……


理解nextval数组:

这个就比next数组容易理解多了,还是拿上面的例子:模式串为ababaa,next数组为:011234,nextval数组为:010104;

例如,当第三位的a失配时,根据next数组要回溯到第一位的a,但是当第三位的a失配时,说明该元素不可能是a,回溯到第一位的a匹配则是多余操作,可以直接跳到它的next;当第六位的a失配时,根据next数组要回溯到第四位的b,因为a失配,只能说明该元素不是a,并不知道它是不是b,所以还要进行匹对;

代码如下:(应该不难理解)

void getNextval(int nextval[], int next[], string s)
{
    nextval[0]=-1;
    for(int i=1; i<s.length(); i++)
    {
        if(s[i]==s[next[i]])//该元素等于next里面的元素,因为字符串的首索引是0
        {
            nextval[i]=nextval[next[i]];
        }
        else
        {
            nextval[i]=next[i];
        }
    }
}

最后附上本题的ac代码:

class Solution {
public:
    int strStr(string haystack, string needle)
    {
        int next[10007]={0}, nextval[10007]={0};
        getNext(next, needle);
        getNextval(nextval, next, needle);
        int i=-1, j=-1;//i是主串,j是模式串
        int flag=0;//记录是否匹配模式串
        while(1)
        {
            if(j==-1||haystack[i]==needle[j])
            {
                i++;
                j++;
            }
            else
            {
                j=nextval[j];
            }
            if(j==needle.length())//匹配成功
            {
                flag=i-needle.length();
                return flag;
            }
            if(i==haystack.length())return -1;
        }
    }
    void getNext(int next[], string s)
    {
        next[0]=-1;
        int i=0, j=-1;
        while(i<s.length())
        {
            if(j==-1||s[i]==s[j])//前面都匹配
            {
                i++;
                j++;
                next[i]=j;
            }
            else
            {
                j=next[j];
            }
        }
    }
    void getNextval(int nextval[], int next[], string s)
    {
        nextval[0]=-1;
        for(int i=1; i<s.length(); i++)
        {
            if(s[i]==s[next[i]])//该元素等于next里面的元素,因为字符串的首索引是0
            {
                nextval[i]=nextval[next[i]];
            }
            else
            {
                nextval[i]=next[i];
            }
        }
    }
};

可能大家还有一点疑惑:为什么我的下标是从-1开始,因为字符串的首字符下标是0,而书上的那些例题讲解都是用字符数组存储字符串,它的首字符下标是从1开始。

建议看懂理解了的同学去力扣重新做一下这道题,加深印象。希望能帮助到大家,欢迎指正!

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

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

相关文章

基于pytorch实现手写数字识别

1&#xff0c;先安装pytorch&#xff0c;在pytorch环境中安装库&#xff1a; 1&#xff09;进入所安装的pytorch环境&#xff0c;我的是pytorch 所以激活它&#xff1a; conda activate pytorch 2&#xff09;使用pip安装numpy,torch,torchvision,matplotlib库 pip instal…

【MDVRP多站点物流配送车辆路径规划问题(带容量限制)】基于遗传算法GA求解

课题名称&#xff1a;基于遗传算法求解带容量限制的多站点的物流配送路径问题MDVRP 版本时间&#xff1a;2023-03-12 代码获取方式&#xff1a;QQ&#xff1a;491052175 或者 私聊博主获取 模型描述&#xff1a; 15个城市中&#xff0c;其中北京&#xff0c;长沙和杭州三座…

Android 多桌面图标启动, 爬坑点击打开不同页面

备注 &#xff1a; MainActivity 正常带界面的UI MainActivityBt 和 MainActivityUsb 是透明的&#xff0c;即 android:theme"style/TranslucentTheme" ###场景1:只有MainActivity 设置成&#xff1a;android:launchMode"singleTask" 点击顺序&#xff1…

02-pycharm详细安装教程(大妈看了都会)

目录 1.官方下载pycharm 2.开始安装pycharm 3.开始运行pycharm 1.官方下载pycharm 官方&#xff1a;https://www.jetbrains.com/zh-cn/pycharm/download/?sectionwindows 提示&#xff1a;安装pycharm之前建议先安装python&#xff0c;因为pycharm要基于python环境才能运…

azure devops工具实践分析

对azure devops此工具的功能深挖&#xff0c;结合jira的使用经验的分析 1、在backlog的功能描述&#xff0c;可理解为需求项&#xff0c;这里包括了bug&#xff0c;从开发的角度修复bug也是个工作项&#xff0c;所以需求的范围是真正的需求&#xff08;开发接收到的已经确认的…

二叉树的前序后序中序层序

文章目录 一、二叉树的前序遍历二、二叉树的中序三、后序四、层序 引言&#xff1a;首先我们讲一下什么是二叉树的前序中序后序层序 前序&#xff1a;从 根 左子树 右子树访问 中序&#xff1a;从 左子树 根 右子树访问 后序&#xff1a;从 左子树 右子树 根访问 等到根为空的…

java面试题(spring框架篇)(黑马 )

树形图&#xff1a; 一、Spring框架种的单例bean是线程安全吗&#xff1f; Service Scope("singleton") public class UserServiceImpl implements UserService{ } singleton:bean在每个Spring IOC容器中只有一个实例 protype&#xff1a;一个bean的定义可以有多个…

使用 Grafana 使用JSON API 请求本地接口 报错 bad gateway(502)解决

一 . 问题&#xff1a; 在用docker部署Grafana 来实现仪表盘的展示&#xff0c;使用到比较多的就是使用JAON API插件调用本地部署的API&#xff0c;比如访问localhost下的 /test_data 接口&#xff0c;一般我们使用的是http://localhost:8080/test_data&#xff0c; 但是在访…

Python测试框架pytest介绍用法

1、介绍 pytest是python的一种单元测试框架&#xff0c;同自带的unittest测试框架类似&#xff0c;相比于unittest框架使用起来更简洁、效率更高 pip install -U pytest 特点&#xff1a; 1.非常容易上手,入门简单,文档丰富&#xff0c;文档中有很多实例可以参考 2.支持简单的单…

计算机网络-第2章 物理层

本章内容&#xff1a;物理层和数据通信的概念、传输媒体特点&#xff08;不属于物理层&#xff09;、信道复用、数字传输系统、宽带接入 2.1-2.2 物理层和数据通信的概念 物理层解决的问题&#xff1a;如何在传输媒体上传输数据比特流&#xff0c;屏蔽掉传输媒体和通信手段的差…

Java学习27--IDEA常用快捷键

智能显示相关提示&#xff1a;altenter,用来快速生成Scanner&#xff0c;或者new object等等&#xff0c;也可以爆红线求提示 代码模板大全ctrlj 可以快速生成try catch finally模块的surround with&#xff1a;ctrlaltt(我换成了altc) 生成getter/setter/构造器等结构-genera…

武器大师——操作符详解(下)

目录 六、单目操作符 七、逗号表达式 八、下标引用以及函数调用 8.1.下标引用 8.2.函数调用 九、结构体 9.1.结构体 9.1.1结构的声明 9.1.2结构体的定义和初始化 9.2.结构成员访问操作符 9.2.1直接访问 9.2.2间接访问 十、操作符的属性 10.1.优先性 10.2.结合性 …

【MySQL】SQL 优化

MySQL - SQL 优化 1. 在 MySQL 中&#xff0c;如何定位慢查询&#xff1f; 1.1 发现慢查询 现象&#xff1a;页面加载过慢、接口压力测试响应时间过长&#xff08;超过 1s&#xff09; 可能出现慢查询的场景&#xff1a; 聚合查询多表查询表数据过大查询深度分页查询 1.2 通…

2023 版王道单科书勘误汇总(3.30)

注:因2023版对题目编号做了优化“历年真题全部放最后、且按年份排序”&#xff0c;以方便大家根据需要保留某些年份的真题作为最后的模拟。所以造成了一些题目和解析的编号错误。 数据结构: P11 P20 P56 P278 P326 “2.”中第 3 行”题 5改成”9”&#xff0c;第6行”题 8”改成…

线性表——单链表的增删查改

本节复习链表的增删查改 首先&#xff0c; 链表不是连续的&#xff0c; 而是通过指针联系起来的。 如图&#xff1a; 这四个节点不是连续的内存空间&#xff0c; 但是彼此之间使用了一个指针来连接。 这就是链表。 现在我们来实现链表的增删查改。 目录 单链表的全部接口…

【EAI 027】Learning Interactive Real-World Simulators

Paper Card 论文标题&#xff1a;Learning Interactive Real-World Simulators 论文作者&#xff1a;Mengjiao Yang, Yilun Du, Kamyar Ghasemipour, Jonathan Tompson, Leslie Kaelbling, Dale Schuurmans, Pieter Abbeel 作者单位&#xff1a;UC Berkeley, Google DeepMind, …

探索设计模式的魅力:备忘录模式揭秘-实现时光回溯、一键还原、后悔药、历史的守护者和穿越时空隧道

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;并且坚持默默的做事。 备忘录模式揭秘-实现时光回溯、一键还原、后悔药和穿越时空隧道 文章目录 一、案例场景&…

19.1 SpringBoot入门

19.1 SpringBoot入门 1. SpringBoot1.1 简介1.2 核心特点1.3 SpringBoot演变1.4 SpringBoot版本1. SpringBoot 1.1 简介 1.2 核心特点

【系统分析师】-计算机组成结构

1、计算机结构 2、存储系统 Cache是访问最快 DRAM是存取最快 先来先服务 FCFS&#xff1a;按照磁道号访问顺序 最短寻道时间优先SSTF&#xff1a;查找下一个最少的磁道数。柱面相同找磁头、磁头相同找扇区 3、数据传输控制方式 4、总线 总线&#xff1a; 分 时 传 输 &#…

十四、计算机视觉-形态学梯度

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、梯度的概念二、梯度的应用三、梯度如何实现 一、梯度的概念 形态学梯度&#xff08;Morphological Gradient&#xff09;是数字图像处理中的一种基本操作&…