当前位置: 首页 > news >正文

LeetCode 面试经典 150_数组/字符串_找出字符串中第一个匹配项的下标(23_28_C++_简单)(KMP 算法)

LeetCode 面试经典 150_数组/字符串_找出字符串中第一个匹配项的下标(23_28_C++_简单)

    • 题目描述:
    • 输入输出样例:
    • 题解:
      • 解题思路:
        • 思路一(暴力破解):
        • 思路二(KMP 算法):
      • 代码实现
        • 代码实现(思路一(暴力破解)):
        • 代码实现(思路二(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 <= 104
haystack 和 needle 仅由小写英文字符组成

题解:

解题思路:

思路一(暴力破解):

1、从左往右进行核对,找出 needle 字符串的第一个匹配项的下标。如果 needle 不是 haystack 的一部分,则返回 -1。

2、复杂度分析:
① 时间复杂度:O(n×m),其中 n 是字符串 haystack 的长度,m 是字符串 needle 的长度。最坏情况下我们需要将字符串 needle 与字符串 haystack 的所有长度为 m 的子串均匹配一次。
② 空间复杂度:O(1)。

思路二(KMP 算法):

1、next 数组用于存储每个位置的最长前缀后缀匹配长度。这样在匹配过程中,如果出现不匹配的情况,可以利用这个数组跳过一些无效的比较

:主串:abababcaa ,子串:ababc
next=[0,0,1,2,0]
abababcaa
ababc          下标为4,通过next[4-1] = 2 来跳转下标 。
abababcaa
    ababc

ababc 的前缀为 a,ab,aba,abab。后缀为 c,bc,abc,babc。
next 数组是对子串进行的处理。
① a无前后缀 ,next[0]=0;
② ab 前缀 a,后缀 b 不匹配,next[1]=0;
③ aba 最长匹配前缀 a,最长匹配后缀 a ,next[2]=1;
④ abab 最长匹配前缀 ab ,最长匹配后缀 ab ,next[3]=2;
⑤ ababc 无匹配前后缀,next[4]=0;

感觉这个博主的 kmp 讲的比较好(点击跳转)

2、复杂度分析
① 时间复杂度:O(n+m),其中 n 是字符串 haystack 的长度,m 是字符串 needle 的长度。我们至多需要遍历两字符串一次。
② 空间复杂度:O(m),其中 m 是字符串 needle 的长度。next数组。

代码实现

代码实现(思路一(暴力破解)):
class Solution1 {
public:int strStr(string haystack, string needle) {// 如果 needle 比 haystack 长,直接返回 -1if (needle.size() > haystack.size()) return -1;// 计算 haystack 剩余的长度,最多能够开始匹配的次数int count = haystack.size() - needle.size() + 1;// 遍历 haystack 中的每个起始位置for (int haystack_index = 0; haystack_index < count; haystack_index++) {int haystack_ptr = haystack_index;  // haystack 的指针int needle_ptr = 0;   // needle 的指针// 如果 haystack 和 needle 的当前字符相等,则继续比较下一个字符while (needle_ptr < needle.size() && haystack[haystack_ptr] == needle[needle_ptr]) {haystack_ptr++;  // haystack 的指针向后移动needle_ptr++;    // needle 的指针向后移动}// 如果 needle 完全匹配,返回 haystack_indexif (needle_ptr == needle.size()) {return haystack_index;}}// 如果没有找到匹配,返回 -1return -1;}
};
代码实现(思路二(KMP 算法)):
class Solution2 {
public:int strStr(string haystack, string needle) {int n = haystack.size(), m = needle.size();  // 获取haystack和needle的长度// 如果needle为空,返回0,根据题意要求if (m == 0) {return 0;}vector<int> next(m);  // 用来存储needle的"部分匹配表"// 计算next数组,next数组表示的是子串中每个位置的最长前后缀匹配的长度// next[i]表示needle[0..i]的最长前后缀匹配的长度for (int i = 1, j = 0; i < m; i++) {  // i从1开始,因为next[0]默认是0// 如果字符不匹配,回溯j指针,利用已计算的next数组避免重新比较while (j > 0 && needle[i] != needle[j]) {j = next[j - 1];  // 回溯到next[j-1]的位置}// 如果字符匹配,移动j指针if (needle[i] == needle[j]) {j++;}// 记录当前的部分匹配长度next[i] = j;}// 接下来用next数组在haystack中进行匹配// i遍历haystack,j遍历needlefor (int i = 0, j = 0; i < n; i++) {// 如果haystack[i]和needle[j]不匹配,回溯jwhile (j > 0 && haystack[i] != needle[j]) {j = next[j - 1];  // 回溯到next[j-1]的位置}// 如果匹配,继续比较下一个字符if (haystack[i] == needle[j]) {j++;}// 如果j到达needle的末尾,表示找到了匹配的子串if (j == m) {return i - m + 1;  // 返回匹配位置的起始下标}}// 如果匹配失败,返回-1return -1; }
};
以思路一为例进行调试
#include<iostream>
#include<unordered_map>
#include<vector>
using namespace std;class Solution1 {
public:int strStr(string haystack, string needle) {// 如果 needle 比 haystack 长,直接返回 -1if (needle.size() > haystack.size()) return -1;// 计算 haystack 剩余的长度,最多能够开始匹配的次数int count = haystack.size() - needle.size() + 1;// 遍历 haystack 中的每个起始位置for (int haystack_index = 0; haystack_index < count; haystack_index++) {int haystack_ptr = haystack_index;  // haystack 的指针int needle_ptr = 0;   // needle 的指针// 如果 haystack 和 needle 的当前字符相等,则继续比较下一个字符while (needle_ptr < needle.size() && haystack[haystack_ptr] == needle[needle_ptr]) {haystack_ptr++;  // haystack 的指针向后移动needle_ptr++;    // needle 的指针向后移动}// 如果 needle 完全匹配,返回 haystack_indexif (needle_ptr == needle.size()) {return haystack_index;}}// 如果没有找到匹配,返回 -1return -1;}
};int main(int argc, char const *argv[])
{string haystack="a";string needle= "a";Solution1 s;cout<<s.strStr(haystack,needle)<<endl;return 0;
}

LeetCode 面试经典 150_数组/字符串_找出字符串中第一个匹配项的下标(23_28)原题链接
欢迎大家和我沟通交流(✿◠‿◠)

http://www.dtcms.com/a/347052.html

相关文章:

  • 「ECG信号处理——(24)基于ECG和EEG信号的多模态融合疲劳分析」2025年8月23日
  • 构建真正自动化知识工作的AI代理
  • 日志搜索系统前端页面(暂无后端功能)
  • 【leetcode】92. 反转链表2
  • k8s总结
  • Sentinel相关记录
  • 语义通信高斯信道仿真代码
  • uniapp对接一键登录
  • 【数据结构】布隆过滤器的概率模型详解及其 C 代码实现
  • uniapp 页面跳转及字符串转义
  • uniApp对接实人认证
  • uniapp mixins的使用
  • Java接口响应速度优化
  • Python SystemVerilog (Python SV)
  • mysql没有mvcc之前遇到了什么问题
  • CSS变量
  • k8sday15
  • 【typenum】 24 去除尾部零的特性(private.rs片段)
  • [激光原理与应用-320]:结构设计 - Solidworks - 软件工具UI组织的核心概念
  • 【软件设计模式】策略模式
  • 【MongoDB与MySQL对比】
  • 【React ✨】从零搭建 React 项目:脚手架与工程化实战(2025 版)
  • SpringBoot applicationContext.getBeansOfType获取某一接口所有实现类,应用于策略模式
  • Claude Code快捷键介绍(Claude Code命令、Claude Code指令、Claude Code /命令、Claude命令、Claude指令)
  • GEO优化供应商:AI搜索时代的“答案”构建与移山科技的引领,2025高性价比实战指南
  • LeetCode Hot 100 第二天
  • GaussDB 数据库架构师修炼(十八) SQL引擎-计划管理-SQL PATCH
  • MSPM0G3507环境搭建
  • 【基础-判断】设计师在设计动效时,点击响应性、运动帧率、跟手性不需要设定,根据不同机型的性能能力系统设定即可
  • 以太坊智能合约地址派生方式:EOA、CREATE 和 CREATE2