(第13、14天)【leetcode题解】#右旋字符串 28、找出字符串中第一个匹配项的下标

2024-05-09 20:04

本文主要是介绍(第13、14天)【leetcode题解】#右旋字符串 28、找出字符串中第一个匹配项的下标,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 右旋字符串
    • 题目描述
    • 思路
    • 代码
    • 思考
  • 28、找出字符串中第一个匹配项的下标
    • 题目描述
    • 暴力匹配
      • 思路
      • 代码
    • KMP算法
      • 思路
      • 代码
      • 难点回顾

右旋字符串

题目描述

  • 字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。
  • 给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。
  • 例如,对于输入字符串 “abcdefg” 和整数 2,函数应该将其转换为 “fgabcde”。

思路

  • 思路一:开辟新的空间,拆分原字符串按要求放入新空间。
  • 思路二:不开辟新空间,原地反转。先全部反转,之后局部反转。

本题的解法使用思路二。

代码

版本一

#include<iostream>
#include<algorithm>
using namespace std;int main() {int k;string s;cin >> k;cin >> s;reverse(s.begin(), s.end());//整体反转reverse(s.begin(), s.begin() + k);//反转前一段,反转的长度为kreverse(s.begin() + k, s.end());//反转后一段cout << s << endl;
}

版本二:反转顺序发生改变。先局部反转,再整体反转。

#include<iostream>
#include<algorithm>
using namespace std;int main() {int k;string s;cin >> k;cin >> s;int len = s.size();//获取字符串长度reverse(s.begin(), s.begin() + len - k);//反转前一段reverse(s.begin() + len - k, s.end());//反转后一段reverse(s.begin(), s.end());//整体反转cout << s << endl;
}

时间复杂度:O(n);翻转操作需要遍历数量级为n的次数。
空间复杂度:O(1);原地翻转,没有开辟额外内存空间。

思考

1.字符串的操作和数组有共性:需要下标访问、各种操作都是基于使用下标进行的元素修改。
2. 目前所遇到的关于字符串的题目都是类似于翻转、改变字符和位置等。这都需要使用指针(下标),双指针是常用的,它可以帮助原地修改字符串,还可以降低时间复杂度。

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

题目描述

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

题目类型为字符串匹配

暴力匹配

思路

  • 将字符串needlehaystack中的所有子串逐个匹配。
  • 为了减少不必要的匹配,一旦匹配失败,立即跳入下一个子串的匹配。

代码

class Solution {
public:int strStr(string haystack, string needle) {int n = haystack.size(), m = needle.size();for (int i = 0; i + m <= n; i++) {//从i开始遍历m个字符,如果从i开始不足m个字符,表明已经没有符合要求的子串bool flag = true;//进行匹配操作for (int j = 0; j < m; j++) {//从0开始遍历子串//如果有一个不相同if (haystack[i + j] != needle[j]) {flag = false;break;//结束本次子串的匹配}}if (flag) {return i;}}return -1;}
};

时间复杂度:O(n*m);最多遍历全部的本文串,并在每次遍历文本串的内部遍历模式串。
空间复杂度:O(1);

KMP算法

思路

  1. 总思路:遍历文本串和模式串,匹配模式串。当遇到字符不匹配的情况时,根据前缀表回退到模式串已经匹配好的位置继续匹配。
  2. 前缀表:长度跟模式串相同,记录每个子串的最长公共前后缀长度。使用最长公共前后缀长度,可以知道回退到哪个位置是已经匹配好的位置,从这个位置开始继续匹配即可,可以提高匹配效率。
  3. 根据前缀表匹配:循环遍历文本串,当前遍历到的字符匹配则移动到下一个字符继续匹配;当前遍历到的字符不匹配则将模式串回退到已经匹配好的位置。
  4. 为什么根据前缀表回退有效:文本串和模式串当前遍历到的字符都匹配,然后移动到下一位,发现不匹配。那么这时就要把模式串回退到与当前文本串前一位及之前匹配好的字符串的位置
  • 根据前后缀相等得到的前缀表记录的长度保证了回退到的位置前n为字符串都是相同的。
  • 例如:当前文本串最后两位为bb,模式串为bbabb,那么根据前缀表可以将模式串回退到bba的位置,a的前面还是bb,和之前匹配失败时前面的字符串相同,那么就可以从这个位置继续之前的匹配,将a与文本串中bb后的字符进行匹配就可以了。
  • 如果继续匹配失败,那就继续往前回退,直到回退到模式串的起点。

代码

版本一

class Solution {
public://完善前缀表//next为指向前缀表中元素的指针;s为模式串  void getNext(int* next,string& s) {int j = -1;//j指向前缀的最后一个元素。j是前缀最后一个元素的下标next[0] = j;//前缀表第一个元素//从第二个元素开始完善前缀表for (int i = 1; i < s.size(); i++) {//i指向后缀的最后一个元素,也是当前遍历到字符串的位置//前后缀不相同时(j=-1时,当前字符串长度为1,前后缀必然相等)while (j >= 0 && s[i] != s[j + 1]) {j = next[j];//当遇到不匹配的情况时,查找前一位字符对应下标前缀表的元素进行回退}//前后缀相同时if (s[i] == s[j + 1]) {j++;//将j向后移动}next[i] = j;//将当前遍历到位置的字符串的最长公共字符串长度放入前缀表中}}int strStr(string haystack, string needle) {if (needle.size() == 0) return 0;vector<int> next(needle.size());//创建前缀表getNext(&next[0], needle);//将前缀表补充完整//开始匹配字符串int j = -1;for (int i = 0; i < haystack.size(); i++) {//i指向haystack//不匹配时,回退while (j >= 0 && haystack[i] != needle[j + 1]) j = next[j]; //匹配时,i和j同时向后移动if (haystack[i] == needle[j + 1]) j++;//匹配完成时,返回下标if (j == (needle.size() - 1)) return (i - needle.size() + 1);}return -1;}
};

版本二

class Solution {
public:void getNext(int* next, string& s) {int j = 0;next[0] = j;for (int i = 1; i < s.size(); i++) {//前后缀不相同while (j >= 1 && s[i] != s[j]) j = next[j - 1];//找到前一个字符对应的最长公共前后缀长度,进行回退//前后缀相同if (s[i] == s[j]) j++;next[i] = j;}}int strStr(string haystack, string needle) {if (needle.size() == 0) return 0;vector<int> next(needle.size());//前缀表getNext(&next[0], needle);//开始匹配int j = 0;for (int i = 0; i < haystack.size(); i++) {//不匹配时while (j >= 1 && haystack[i] != needle[j]) j = next[j - 1];//回退//匹配时if (haystack[i] == needle[j]) j++;//i和j同时后移//匹配完成时if (j == needle.size()) return (i - needle.size() + 1);}return -1;}
};

时间复杂度:O(n+m);匹配时只遍历一次文本串,每遍历到一个字符只做一次判断和常数级的操作。
空间复杂度:O(m);需要创建前缀表存储模式串的最长公共前后缀长度。

难点回顾

  1. 匹配过程中怎样使用前缀表:当前位置(j)字符匹配失败,找到前一个字符(j-1)在前缀表中的值,使用这个值当作索引退回到模式串的指定位置再次开始匹配。
  2. 怎么得到前缀表(实现):分两种实现。
  • 正常实现:
    前提j不光代表前缀末尾索引,还代表最长公共前后缀长度
    第一步:前缀表第一个位置初始化为0(这时只有一个字符,最长公共前后缀长度必然为0)。
    第二步:从模式串第二个位置开始(索引为1)比较前后缀末尾字符是否相同。
    第三步:当前后缀末尾元素相同时(即s[i]==s[j]),共同移动到下一个位置。同时将j(这时代表最长公共前后缀长度)赋值给前缀表索引i所在的位置。
    第四步:当前后缀末尾元素不相同时(即s[i]!=s[j]),在前缀表中查找j之前的元素对应索引位置对应的值,把这个值当作索引赋给j,完成回退操作。
    第五步:在遍历整个模式串的过程中,持续完成以上条件判断及其相应操作。
  • 减一实现:
    整体思路相同,但因为把前缀表每个数都减一,导致使用前缀表进行的回退操作略有差别:前缀表中索引位置j的值就代表要回退的位置,而不是j-1位置。
    使用j代表索引时需要加一。

这篇关于(第13、14天)【leetcode题解】#右旋字符串 28、找出字符串中第一个匹配项的下标的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Java 字符数组转字符串的常用方法

《Java字符数组转字符串的常用方法》文章总结了在Java中将字符数组转换为字符串的几种常用方法,包括使用String构造函数、String.valueOf()方法、StringBuilder以及A... 目录1. 使用String构造函数1.1 基本转换方法1.2 注意事项2. 使用String.valu

python修改字符串值的三种方法

《python修改字符串值的三种方法》本文主要介绍了python修改字符串值的三种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录第一种方法:第二种方法:第三种方法:在python中,字符串对象是不可变类型,所以我们没办法直接

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

C#中字符串分割的多种方式

《C#中字符串分割的多种方式》在C#编程语言中,字符串处理是日常开发中不可或缺的一部分,字符串分割是处理文本数据时常用的操作,它允许我们将一个长字符串分解成多个子字符串,本文给大家介绍了C#中字符串分... 目录1. 使用 string.Split2. 使用正则表达式 (Regex.Split)3. 使用

Java中JSON字符串反序列化(动态泛型)

《Java中JSON字符串反序列化(动态泛型)》文章讨论了在定时任务中使用反射调用目标对象时处理动态参数的问题,通过将方法参数存储为JSON字符串并进行反序列化,可以实现动态调用,然而,这种方式容易导... 需求:定时任务扫描,反射调用目标对象,但是,方法的传参不是固定的。方案一:将方法参数存成jsON字

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测