代码算法训练营day9 | 28. 实现 strStr() 、459.重复的子字符串

day9:

  • 28. 实现 strStr()
      • KMP的主要应用:
      • 什么是前缀表:
        • 前缀表是如何记录的:
      • 如何计算前缀表:
      • 构造next数组:
        • 1、初始化
        • 2、处理前后缀不相同的情况
        • 3、处理前后缀相同的情况
      • 代码:
  • 459.重复的子字符串(先不做了,)

28. 实现 strStr()

题目链接
状态:KMP不太懂
文档:programmercarl.com

思路:
KMP的经典思想就是:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。

KMP的主要应用:

KMP主要应用在字符串匹配上。
KMP的主要思想是当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。

什么是前缀表:

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

举个例子:(在文本串中查找是否存在模式串)
文本串:aa b aa baafa — 模式串:aa b aa f
可以看出,文本串中第六个字符b 和 模式串的第六个字符f,不匹配了。如果暴力匹配,发现不匹配,此时就要从头匹配了。
但如果使用前缀表,就不会从头匹配,而是从上次已经匹配的内容开始匹配。

文本串中的aabaa已经和模式串中的aabaa匹配好了,只有最后一个字符不匹配
那么就要从上次已经匹配好的内容开始匹配,上次和模式串中的 f 前的aa匹配好了的是文本串中的b,所以从模式串中第三个字符b继续开始匹配。

前缀表是如何记录的:

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

那么什么是前缀表:记录下标 i 之前(包括i)的字符串中,有多大长度的相同前缀后缀。

如何计算前缀表:

前缀表
注意字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串
可以看出模式串与前缀表对应位置的数字表示的就是:下标 i 之前(包括i)的字符串中,有多大长度的相同前缀后缀。

找到的不匹配的位置, 那么此时我们要看它的前一个字符的前缀表的数值是多少。
为什么要前一个字符的前缀表的数值呢,因为要找前面字符串的最长相同的前缀和后缀。所以要看前一位的 前缀表的数值。
前一个字符的前缀表的数值是几, 所以把下标移动到下标为几的位置继续匹配。

next数组既可以就是前缀表,也可以是前缀表统一减一(右移一位,初始位置为-1)。

构造next数组:

我们定义一个函数getNext来构建next数组,函数参数为指向next数组的指针,和一个字符串。 代码如下:

void getNext(int* next, const string& s)

构造next数组其实就是计算模式串s,前缀表的过程。 主要有如下三步:

  1. 初始化
  2. 处理前后缀不相同的情况
  3. 处理前后缀相同的情况
1、初始化

定义两个指针 i 和 j,j 指向前缀末尾位置,i 指向后缀末尾位置。
然后还要对next数组进行初始化赋值,如下:

int j = -1;
next[0] = j;

next[i] 表示 i(包括i)之前最长相等的前后缀长度(其实就是j)

2、处理前后缀不相同的情况

因为j初始化为-1,那么i就从1开始,进行s[i] 与 s[j+1]的比较。
为什么是 i 和 j+1 去比较呢?既然前缀表统一减一了,那么回退的时候也会多回退1,所以就要在 j 上下功夫了,让 j+1,每次比较的时候都比较 j 的后一位。
遍历模式串s的循环下标i 要从 1开始,代码如下:

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

如果 s[i] 与 s[j+1]不相同,也就是遇到 前后缀末尾不相同的情况,就要向前回退。
怎么回退呢?
next[j]就是记录着j(包括j)之前的子串的相同前后缀的长度。
那么 s[i] 与 s[j+1] 不相同,就要找 j+1前一个元素在next数组里的值(就是next[j])。
所以,处理前后缀不相同的情况代码如下:

while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了j = next[j]; // 向前回退
}
3、处理前后缀相同的情况

如果 s[i] 与 s[j + 1] 相同,那么就同时向后移动i 和j,说明找到了相同的前后缀,
所有情况处理结束后,还要将j(前缀的长度)赋给next[i], 因为next[i]要记录相同前后缀的长度。

if (s[i] == s[j + 1]) { // 找到相同的前后缀j++;
}
next[i] = j;

最后整体构建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]}
}

代码:

class Solution {
public://创建next数组 整体-1void getNext(int* next,string& s){//初始化(后缀i,前缀j,next数组)int j = -1;next[0] = j;//i不能=0,因为还要和j进行比较for(int i=1;i<s.size();i++){//前后缀不相等while(j >= 0 && s[i] != s[j+1]){//j向前一个next的值进行回退j = next[j];}//前后缀相等if(s[i] == s[j+1]){j++; //j向前走一位,同时i也向前走一位}//更新next值next[i] = j; //因为j已经++了,所以已经表示相对应的串的长度了}}int strStr(string haystack, string needle) {if(needle.size() ==0){return 0;}int next[needle.size()];getNext(next,needle); //获取needle的next数组//在文本串s里 找是否出现过模式串tint j = -1; //因为next数组里记录的起始位置为-1//i是从0开始的,因为要从头比for(int i = 0;i<haystack.size();i++){//如果不匹配while(j >= 0 && haystack[i] != needle[j+1]){//j>=0才行,不然next[j]就是无效数据了j = next[j];}//匹配上了if(haystack[i] == needle[j+1]) j++;if(j == needle.size()-1) //比的是j+1 j++后就是j+1的位置return (i-needle.size()+1);}return -1;}
};

459.重复的子字符串(先不做了,)

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

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

相关文章

Python算法100例-4.1 将真分数分解为埃及分数

完整源代码项目地址&#xff0c;关注博主私信源代码后可获取 1.问题描述2.问题分析3.算法设计4.补充知识点5.确定程序框架6.完整的程序 1&#xff0e;问题描述 现输入一个真分数&#xff0c;请将该分数分解为埃及分数。 2&#xff0e;问题分析 真分数&#xff08;a proper…

面向控制台编程?Java的GUI开发

记得之前刚开始学习Java&#xff0c;按部就班去阅读《Java核心技术》这本书的时候&#xff0c;总是听别人提起&#xff0c;java swing那一章不用看了。然后直到对着控制台编程了半年&#xff0c;回来捡起了Swing图形界面&#xff0c;跟着网上搞了坦克大战的游戏&#xff0c;总觉…

ECMAscript6学习

ECMAscript6介绍 ECMA是一个浏览器脚本标准制定的公司&#xff0c;Netscape 创造了 JavaScript 由于商标原因&#xff0c; 后面ECMA公司取名ECMAscript 1 发布&#xff0c;JavaScript 也就是 ECMAscript.到现在最新的版本是6&#xff0c;简称es6. 新增let 与const let 与const …

Unity在UGUI上通过绘制网格顶点自由画线

该插件的实现是使用UI组件的绘图API来动态生成和修改几何形状&#xff0c;可自由动态更改画线的粗细、拐角圆滑度、颜色&#xff0c;自由增减节点&#xff0c;不额外增加gameobject&#xff0c;并且在原生的UGUI上以ScreenSpace-Overlay的状态下&#xff0c;显示效果如下所示 …

每日一题:LeetCode2.两数相加

给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都不会以 0 …

C#控制台贪吃蛇

Console.Write("");// 第一次生成食物位置 // 随机生成一个食物的位置 // 食物生成完成后判断食物生成的位置与现在的蛇的身体或者障碍物有冲突 // 食物的位置与蛇的身体或者障碍物冲突了&#xff0c;那么一直重新生成食物&#xff0c;直到生成不冲突…

说说JVM的垃圾回收机制

简介 垃圾回收机制英文为Garbage Collection, 所以我们常常称之为GC。那么为什么我们需要垃圾回收机制呢&#xff1f;如果大家有了解过Java虚拟机运行时区域的组成(JVM运行时存在&#xff0c;本地方法栈&#xff0c;虚拟机方法栈&#xff0c;程序计数器&#xff0c;堆&#xf…

麒麟系统Redis7.2哨兵集群部署

redis哨兵集群部署 1、原理 Redis 哨兵模式是指在 Redis 集群中,有一组专门的进程(即哨兵进程)负责监控主节点和从节点的状态,并在发现故障时自动进行故障转移,以保证 Redis 集群的高可用性。 Redis 提供了哨兵的命令,哨兵命令是一个独立的进程,哨兵进程会周期性地向主…

60 个深度学习教程:包含论文、实现和注释 | 开源日报 No.202

labmlai/annotated_deep_learning_paper_implementations Stars: 44.0k License: MIT annotated_deep_learning_paper_implementations 是一个包含深度学习论文的 60 个实现/教程&#xff0c;附带并排注释&#xff1b;包括 transformers&#xff08;原始、xl、switch、feedbac…

Sentaurus TCAD中SDE的mtt命令

Reflection 配套代码 ; Building mesh (sde:build-mesh "snmesh" "" "nnode_half_msh") ; Reflect the device (system:command "tdx -mtt -x -M 0 -S 0 -ren drainsource nnode_half_msh nnode_msh");----------------------------…

【洛谷 P8661】[蓝桥杯 2018 省 B] 日志统计 题解(滑动窗口+优先队列+双端队列+集合)

[蓝桥杯 2018 省 B] 日志统计 题目描述 小明维护着一个程序员论坛。现在他收集了一份“点赞”日志&#xff0c;日志共有 N N N 行。其中每一行的格式是 ts id&#xff0c;表示在 t s ts ts 时刻编号 i d id id 的帖子收到一个“赞”。 现在小明想统计有哪些帖子曾经是“热…

电脑充电器能充手机吗?如何给手机充电?

电脑充电器可以给手机充电吗&#xff1f; 电脑充电器可以给手机充电&#xff0c;但前提是电脑充电器的功率输出与手机的功率匹配且接口匹配。 假设电脑充电器的输出功率为5V/2A&#xff0c;手机也支持5V/2A的输入功率。 只要接口匹配&#xff0c;就可以使用电脑充电器给手机充…

20240314-2-字符串string

1.最长公共前缀 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 “”。 示例 1: 输入: [“flower”,“flow”,“flight”] 输出: “fl” 示例 2: 输入: [“dog”,“racecar”,“car”] 输出: “” 解释: 输入不存在公共前缀…

玩转键盘鼠标,自动化你的电脑操作 —— 定时执行专家

简介 “定时执行专家”是一款功能强大的定时任务执行软件&#xff0c;除了支持常见的定时关机、重启、执行程序等功能外&#xff0c;还拥有模拟键盘按键和模拟鼠标操作功能&#xff0c;可以让你轻松实现各种自动化操作。 模拟键盘按键功能可以模拟用户的键盘输入&#xff0c;让…

如何用Selenium通过Xpath,精准定位到“多个相同属性值以及多个相同元素”中的目标属性值

前言 本文是该专栏的第21篇,后面会持续分享python爬虫干货知识,记得关注。 相信很多同学,都有使用selenium来写爬虫项目或者自动化页面操作项目。同样,也相信很多同学在使用selenium来定位目标元素的时候,或多或少遇见到这样的情况,就是用Xpath定位目标元素的时候,页面…

第五章、数据库6分

数据库的三级模式和两级映像图 数据模型的三要素&#xff1a;数据结构、数据操作、数据的完整性约束条件 数据库的三级模式和两级映像图 关系代数 函数依赖 平凡函数依赖&#xff1a; 规范化理论 1NF&#xff1a;每个属性都是不可再分的原子 2NF&#xff1a;不存在部分函…

MediaCodec源码分析 状态简单介绍

前言 本文分析MediaCodec.h层的状态机,下篇介绍ACodec状态机,基于7.0代码。 MediaCodec状态介绍 During its life a codec conceptually exists in one of three states: Stopped, Executing or Released. The Stopped collective state is actually the conglomeration of…

我是继续学习编程,还是学数控?

今日话题&#xff0c;继续学习编程&#xff0c;还是学数控&#xff1f;综合来说肯定是软件的待遇和工作环境都要好些。 当然这行有一定的技术门槛&#xff0c;所谓会者不难&#xff0c;难者不会。要入门需要一定的天赋或者说时间&#xff0c;当然 兴趣是最好的老师&#xff0c;…

快慢指针:妙解查找链表的中间结点问题

给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&#xff1a;链表只有一个中间结点&#xff0c;值为 3 。示…

基于Linux内核的socket编程(TCP)的C语言示例

原文地址&#xff1a;https://www.geeksforgeeks.org/socket-programming-cc/ 服务端&#xff1a; #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <unistd.h>#…