【算法练习Day8】 kmp算法找出字符串中第一个匹配项的下标反转字符串中的单词重复的子字符串

news2024/12/26 15:50:38

、​在这里插入图片描述

​📝个人主页:@Sherry的成长之路
🏠学习社区:Sherry的成长之路(个人社区)
📖专栏链接:练题
🎯长路漫漫浩浩,万事皆有期待

文章目录

  • kmp算法
  • 找出字符串中第一个匹配项的下标
  • 重复的子字符串
    • 移动匹配
    • kmp算法
  • 总结:

kmp算法

kmp算法是一种快速查找字符串的算法,主要用途是在一个字符串里查找是否包含另一个字符串,kmp在理解上不是很友好,首先我们要搞清楚什么是前后缀、最长相等前后缀、前缀表、next数组如何实现前缀表,最后就是kmp算法借助前缀表的实现。

前后缀的概念

前缀就是包含第一个字符,而不包含最后一个字符的所有字串都可以被称为前缀。

后缀是包含最后一个字符,且不包含第一个字符的所有字串都可以被称为后缀。

举个例子:hello 前缀可以是h,he,hel,hell 后缀可以是:e,el,ell,ello

最长相等前后缀

最长相等前后缀顾名思义,就是前后缀相等的情况下,我们取最长的那一段。

前缀表,next数组如何实现?

前缀表十分重要,它是KMP算法的核心思想,先说它的作用,它的作用是实现匹配字符串时,如果遇到主串(用于对比的字符串)和模式串(拿来做对比的字符串)某个字符不相等时,将指针返回到上一个最长相等前后缀处再进行对比,这样做的好处是可以使字符不相等时退回某处,而不至于像暴力求解一样一遇到不相等就要重头对比,在主串有一定规律时,此方法显得十分方便。

下面我们再来说一说实现的方法:
定义一个数组名字叫next数组,网上大体有三种方法,第一种就是数组正常存储返回地址,第二种数组存储返回地址-1,第三种将返回地址整体向左移动。

我们说到next数组就是一个前缀表,它来存我们字符串里字符匹配失败时,应该返回的位置用第二种方法实际上是返回到最长相等前缀的位置

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]
    }
}

这一段就是c++的前缀表代码段,我们用j来表示前缀的最长长度为多少,i来向后遍历,当我们这个字符串的两个位置不相等时,判断j是否大于0,因为要涉及到j-1,只要两个位置不相等就使j一直回退,如果两位置相等,那么j向前走一步,意为最长长度加1,然后在数组每一个位置,记录当前匹配失败时,回退到那个位置。

关于kmp算法的使用我们来看具体的例题。

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

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

这道题就是典型可以应用kmp算法的题型

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

第一部分的代码就是上面所说的前缀和,后面的是用循环来遍历,也就是应用前缀和数组next,遍历查找,如果两字符串某个字符不相等,那么就使j一直回退,相等时候j++,当j++到模式串的最后一个字符了,则说明该字符串在主串中,由于字符不相同时,只有j会回退,i仍然向前走,所以当i走完全部,j还没有到到最后一个字符,说明该字符串不存在于主串内。

重复的子字符串

459. 重复的子字符串 - 力扣(LeetCode)

移动匹配

判断字符串s是否由重复子串组成,只要两个s拼接在一起,里面还出现一个s的话,就说明是由重复子串组成。当然,我们在判断 s + s 拼接的字符串里是否出现一个s的的时候,要刨除 s + s 的首字符和尾字符,这样避免在s+s中搜索出原来的s,我们要搜索的是中间拼接出来的s。

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        string t = s + s;
        t.erase(t.begin()); t.erase(t.end() - 1); // 掐头去尾
        if (t.find(s) != std::string::npos) return true; // r
        return false;
    }
};

kmp算法

这道题刚看的时候,不太像使用kmp算法的题目,但是可以用kmp算法,其中是有一些技巧的:字符串的相等前后缀,错开的那一部分则为重复子串,用该重复子串重复n次,则可以取得原字符串。这个结论可以模拟一下。用next数组模拟,数组中最后一位存取的就是最长前后缀的长度,用字符串总长度,对总长度减去最长前后缀的长度,再取模,如果能整除,则该字符串是由前x个字符由n次变换得到,若不能整除则说明字符串不是由重复字串得到的。这里使用了前缀表统一减一的实现方式:

class Solution {
public:
    void getNext (int* next, const string& s)
    {
        next[0] = -1;
        int j = -1;
        for(int i = 1;i < s.size(); i++){
            while(j >= 0 && s[i] != s[j + 1]) 
            {
                j = next[j];
            }
            if(s[i] == s[j + 1]) 
            {
                j++;
            }
            next[i] = j;
        }
    }
    bool repeatedSubstringPattern (string s) {
        if (s.size() == 0) {
            return false;
        }
        int next[s.size()];
        getNext(next, s);
        int len = s.size();
        if (next[len - 1] != -1 && len % (len - (next[len - 1] + 1)) == 0) 
        {
            return true;
        }
        return false;
    }
};

总结:

今天我们进行了字符串的复习和双指针的回顾,还有kmp算法的应用,kmp算法还是挺有难度的,相关的思想需要多复习回顾。接下来,我们继续进行算法练习·。希望我的文章和讲解能对大家的学习提供一些帮助。

当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~

在这里插入图片描述

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

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

相关文章

MySQL学习笔记21

MySQL逻辑备份&#xff1a; mysqldump基本备份&#xff1a; 本质&#xff1a;导出的是sql语句文件。 优点&#xff1a;无论是什么存储引擎&#xff0c;都可以用mysqldump备份成sql语句。 缺点&#xff1a;速度较慢&#xff0c;导入的时候出现格式不兼容的突发情况&#xff…

Appium开发

特点 开源免费支持多个平台 IOS(苹果)、安卓App的自动化都支持 支持多种类型的自动化 支持苹果、安卓应用原生界面的自动化支持应用内嵌网络视图的自动化支持手机浏览器(Chrome)中的web网站自动化支持flutter应用的自动化 支持多种编程语言 像selenium一样&#xff0c;可以用多…

全面解读 SQL 优化 - 统计信息

一、简介 数据库中的优化器&#xff08;optimizer&#xff09;是一个重要的组件&#xff0c;用于分析 SQL 查询语句&#xff0c;并生成执行计划。在生成执行计划时&#xff0c;优化器需要依赖数据库中的统计信息来估算查询的成本&#xff0c;从而选择最优的执行计划。以下是关…

一文了解什么是分辨率

分辨率是对显示屏或相机传感器中可以包含的像素&#xff08;图片元素或单个颜色点&#xff09;数量的测量。实际上&#xff0c;分辨率描述了图像或图片的锐度或清晰度。它以水平和垂直方向上可显示的像素数来表示。 表示的分辨率&#xff1a;显示器、打印机和音频 在大多数显示…

外包公司干了2个月,技术倒退两年...

先说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入杭州某软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年8月份&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了三年的功能测试…

JOSEF约瑟DZJ-402 DZY-401导轨式中间继电器 触点形式 两转换 AC、DC220V

DZY(J)-400导轨式中间继电器 系列型号 DZY、DZJ-401 DZY、DZJ-402 DZY、DZJ-403 DZY、DZJ-404 DZY、DZJ-405 DZY、DZJ-406 DZY、DZJ-407 DZY、DZJ-408 DZY、DZJ-409 DZY、DZJ-410 DZY、DZJ-411 DZY、DZJ-412 DZY、DZJ-413 DZY、DZJ-414 DzY、DZJ-415 DZY、DZJ…

嵌入式学习笔记(34)S5PV210中断处理的编程实战

6.8.1中断控制器初始化 主要工作有&#xff1a;第一阶段绑定异常处理程序到异常向量表&#xff1b;禁止所有中断源&#xff1b;选择所有中断类型为IRQ&#xff1b;清理VICnADDR寄存器为0。 为什么要禁止所有中断源&#xff1a; 因为中断一旦打开&#xff0c;因为外部或硬件自…

秦时明月卡牌版攻略,最强的推图阵容推荐

这篇文章提供了关于秦时明月卡牌版游戏最强推图攻略2023的详细信息&#xff0c;以帮助玩家在游戏中创建最强的推图阵容。让我们一起来看看这些新的信息吧。 关注【娱乐天梯】&#xff0c;获取内部福利号 秦时明月卡牌版强大的推图组合提议 推图游戏中&#xff0c;绝对不可忽视的…

ros使用rviz订阅Image类型话题,显示yolov7目标检测结果

开发板深度相机系统Xavierrealsense D455ubuntu18.04 1、首先启动yolov7的ros节点&#xff0c;发布话题/image。 2、终端输入rviz&#xff0c;回车打开rviz&#xff0c;Add——>Image&#xff1a; 3、展开Image选项&#xff0c;选择要订阅的topic&#xff0c;左下角就显示出…

中秋佳节,华为手机如何拍月亮?

今年中秋你在哪里过&#xff1f;又到中秋佳节月圆夜&#xff0c;一年一度最期待的拍月亮环节来了&#xff01;分享几个华为手机拍月亮小技巧&#xff0c;共赏中秋月色~

【Vue2.0源码学习】生命周期篇-挂载阶段(mount)

文章目录 1. 前言2. 挂载阶段分析3. 总结 1. 前言 模板编译阶段完成之后&#xff0c;接下来就进入了挂载阶段&#xff0c;从官方文档给出的生命周期流程图中可以看到&#xff0c;挂载阶段所做的主要工作是创建Vue实例并用其替换el选项对应的DOM元素&#xff0c;同时还要开启对…

DIY手工坊商城小程序的作用有哪些

自己动手做的东西往往更具自豪感&#xff0c;也能锻炼自身的动手能力&#xff0c;而DIY手工坊无疑是小孩子、年轻人喜欢的地方&#xff0c;无论自己做还是成品都具备自由性、不受规格类型局限&#xff0c;比如手机壳、绘画、手工积木、雕塑等。 而对商家来说&#xff0c;经营收…

赋能工业数字化转型|辽宁七彩赛通受邀出席辽宁省工业互联网+安全可控先进制造业数字服务产业峰会

2023年9月25日下午&#xff0c;由软通动力信息技术&#xff08;集团&#xff09;股份有限公司主办的“工业互联网安全可控先进制造业数字服务产业峰会”在辽宁沈阳顺利举办。省市区各级政府、科研院所领导、技术专家、企业高管以及生态合作伙伴代表等齐聚一堂&#xff0c;共同探…

Konva事件机制

前言 不同于HTML或SVG标签可以直接绑定事件&#xff0c;Canvas是使用JavaScript来绘制内容&#xff0c;这意味着其内容没有具体的DOM&#xff0c;所以Canvas渲染引擎都会自己实现一套事件机制。Konva的事件机制支持图形的选中、拖拽等交互处理&#xff0c;同时还支持单个图形对…

微信小程序案例2-1:学生信息

文章目录 &#xff08;二&#xff09;准备图像素材&#xff08;三&#xff09;编写小程序页面结构 单击[确认] 清空页面结构文件index.wxml内容 修改页面配置文件index.json&#xff0c;不适用navigation-bar组件 删除全局配置文件app.json&#xff0c;删除渲染器配置&a…

一键开启默认打印机共享功能,如何用powershell编写一个一键开启windows本地默认打印机共享的脚本

环境: Win10 专业版 Win11专业版 powershell 问题描述: 一键开启默认打印机共享功能,如何用powershell编写一个一键开启windows本地默认打印机共享的脚本 解决方案: 修改Windows注册表来实现打印机共享:(未成功)打开注册表编辑器:按下Win + R组合键,输入"re…

手机,汽车大卖背后,华为生态进入发展新阶段

监制 | 何玺 排版 | 叶媛 从8月底至今&#xff0c;华为手机、汽车的热度就没有消退过。 如今的华为产品&#xff0c;从手机到AR眼镜&#xff0c;从智慧屏到汽车&#xff0c;几乎全线都处在一机难求、一车难求状态。 9月25日举行的华为秋季全场景新品发布会&#xff0c; 更是…

实验三十三、三端稳压器 LM7805 稳压性能的研究

一、题目 LM7805 输出电压、电压调整率、电流调整率以及输出纹波电压的研究。 二、仿真电路 电路如图1所示。集成稳压芯片采用 LM7805CT。 三、仿真内容 &#xff08;1&#xff09;测量图1&#xff08;a&#xff09;LM7805CT 的电压调整率&#xff0c;测量条件为 I O 50…

【数据结构与算法】通过双向链表和HashMap实现LRU缓存 详解

这个双向链表采用的是有伪头节点和伪尾节点的 与上一篇文章中单链表的实现不同&#xff0c;区别于在实例化这个链表时就初始化了的伪头节点和伪尾节点&#xff0c;并相互指向&#xff0c;在第一次添加节点时&#xff0c;不需要再考虑空指针指向问题了。 /*** 通过链表与HashMa…

算法与数据结构-Trie树

文章目录 什么是“Trie 树”&#xff1f;如何实现一棵 Trie 树&#xff1f;Trie 树真的很耗内存吗&#xff1f;Trie 树与散列表、红黑树的比较 什么是“Trie 树”&#xff1f; Trie 树&#xff0c;也叫“字典树”。顾名思义&#xff0c;它是一个树形结构。它是一种专门处理字符…