代码随想录算法训练营第九天 | 28、找出字符串中第一个匹配项的下标、459. 重复的子字符串

本文主要是介绍代码随想录算法训练营第九天 | 28、找出字符串中第一个匹配项的下标、459. 重复的子字符串,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

题目链接:28、找出字符串中第一个匹配项的下标

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

文章讲解/视频讲解:https://programmercarl.com/0028.%E5%AE%9E%E7%8E%B0strStr.html

思路

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

这里摘录一下卡哥教程中的内容。

什么是KMP

说到KMP,先说一下KMP这个名字是怎么来的,为什么叫做KMP呢。

因为是由这三位学者发明的:Knuth,Morris和Pratt,所以取了三位学者名字的首字母。所以叫做KMP。

KMP有什么用

KMP主要应用在字符串匹配上。

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

所以如何记录已经匹配的文本内容,是KMP的重点,也是next数组肩负的重任。

什么是前缀表

KMP代码中的next数组就是一个前缀表。

前缀表有什么作用呢?

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

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

最长公共前后缀

字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。

后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。

如何计算前缀表

对数组中的每一个位置遍历,计算当前位置的前缀和后缀中,最大相等长度,填入前缀表。一个示例如下:

在这里插入图片描述

前缀表如何使用

找到不匹配的位置,此时我们要看它的前一个字符的前缀表的数值是多少。

为什么要前一个字符的前缀表的数值呢,因为要找前面字符串的最长相同的前缀和后缀。

如果前一个字符的前缀表的数值是2,那么把下标移动到下标2的位置继续匹配。

直到找到了和模式串匹配的子串为止。

解题

解这道题分为两步,构造next数组,然后利用next数组进行匹配 。

构造next数组

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

1.初始化

定义两个指针i和j,j指向前缀末尾位置,i指向后缀末尾位置。j初始化为0。

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

因为i指向的是后缀末尾位置,遍历时,i从1开始,如果s[i]与s[j]不相同,即遇到了前后缀不相同的情况,就要向前回退,令j = next[j - 1]。

3.处理前后缀相同的情况

如果s[i]与s[j]相同,则说明遇到了相同的前后缀,那么这时可以将j后移,j++,再将j赋值给next数组,记录在i这个位置,前后缀相等的最大长度。

这一步的过程,我的理解是模式串在进行自匹配。

使用next数组来做匹配

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

定义两个下标i和j,下标j指向模式串的起始位置,i指向文本串当前匹配的末尾位置。j初始化为0。

i从0开始遍历文本串,令s[i]与t[j]做比较,如果不相同,j就要从next数组中寻找下一个匹配的位置,令j = next[j],如果相同,则i和j同时后移。

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

C++实现

class Solution {
public:vector<int> getNext(const string& s){vector<int> next(s.size(), 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;}return next;}int strStr(string haystack, string needle) {if(needle.size() == 0) return 0;if(needle.size() > haystack.size()) return -1;vector<int> next = getNext(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;}
};

459. 重复的子字符串

题目链接:459. 重复的子字符串

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

文章讲解/视频讲解:https://programmercarl.com/0459.%E9%87%8D%E5%A4%8D%E7%9A%84%E5%AD%90%E5%AD%97%E7%AC%A6%E4%B8%B2.html

思路

可以按照KMP的思路,对该字符串s求解其前缀表,即next数组。

然后在前缀表中寻找值连续的第一个下标i,此时说明i及之后的字符串元素都是连续自匹配的,i的值即为模式子串的长度pattenLen。

判断s.size() % pattenLen是否等于0,如果等于0,则说明该字符串可由子串重复构成。注意判断pattenLen的值既不能等于0也不能等于s.size()。

例如:对于字符串s = “babbabbabbabbab”,其next数组为:

[0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

找到值连续的第一个下标i为3,字符串的长为15,15 % 3 = 0,说明可由子串构成,i的大小即为模式子串的长度。

下标i可由next.size() - next[next.size() - 1]得到。

C++实现

class Solution {
public:vector<int> getNext(const string& s){vector<int> next(s.size(), 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;}return next;}bool repeatedSubstringPattern(string s) {if(s.size() == 1) return false;vector<int> next = getNext(s);int pattenLen = next.size() - next[next.size() - 1];return (s.size() % pattenLen == 0) && pattenLen != 0 && pattenLen != next.size();}
};

这篇关于代码随想录算法训练营第九天 | 28、找出字符串中第一个匹配项的下标、459. 重复的子字符串的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/520538

相关文章

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

C#数据结构之字符串(string)详解

《C#数据结构之字符串(string)详解》:本文主要介绍C#数据结构之字符串(string),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录转义字符序列字符串的创建字符串的声明null字符串与空字符串重复单字符字符串的构造字符串的属性和常用方法属性常用方法总结摘

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤