每日算法练习:LeetCode 28. 找出字符串中第一个匹配项的下标 ✅

大家好,我是你们的算法小伙伴。今天我们来练习一道字符串匹配 的经典简单题 ------LeetCode 28. 找出字符串中第一个匹配项的下标。这道题考察字符串子串匹配,是面试中常见的基础题,也是学习 KMP 算法的入门题。


题目描述

给你两个字符串 haystackneedle,请你在 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 <= 10^4
  • haystackneedle 仅由小写英文字符组成

解题思路

方法一:暴力匹配

  1. 遍历 haystack 中所有可能的起始位置 i(范围 [0, len(haystack)-len(needle)])。
  2. 对于每个 i,逐个字符比较 haystack[i:i+m]needlemneedle 长度)。
  3. 若完全匹配,直接返回 i;若遍历完都不匹配,返回 -1

方法二:KMP 算法

利用 "部分匹配表(LPS 数组)"避免重复比较,时间复杂度优化到 O (n+m),适合处理大数据量。

方法三:Java 内置方法

直接使用 String.indexOf() 方法,底层已做优化,代码最简洁。


代码实现

方法一:暴力匹配

复制代码
class Solution {
    public int strStr(String haystack, String needle) {
        int n = haystack.length();
        int m = needle.length();
        // 遍历所有可能的起始位置
        for (int i = 0; i <= n - m; i++) {
            int j = 0;
            // 逐个字符比较
            while (j < m && haystack.charAt(i + j) == needle.charAt(j)) {
                j++;
            }
            // 完全匹配,返回起始下标
            if (j == m) {
                return i;
            }
        }
        // 未找到匹配
        return -1;
    }
}

方法二:KMP 算法

复制代码
class Solution {
    public int strStr(String haystack, String needle) {
        int n = haystack.length();
        int m = needle.length();
        if (m == 0) return 0;
        
        // 1. 构建 LPS 数组(最长前缀后缀数组)
        int[] lps = new int[m];
        int len = 0; // 最长公共前后缀长度
        int i = 1;
        while (i < m) {
            if (needle.charAt(i) == needle.charAt(len)) {
                len++;
                lps[i] = len;
                i++;
            } else {
                if (len != 0) {
                    len = lps[len - 1];
                } else {
                    lps[i] = 0;
                    i++;
                }
            }
        }
        
        // 2. KMP 匹配
        i = 0; // haystack 指针
        int j = 0; // needle 指针
        while (i < n) {
            if (haystack.charAt(i) == needle.charAt(j)) {
                i++;
                j++;
            }
            if (j == m) {
                return i - j; // 找到匹配,返回起始下标
            } else if (i < n && haystack.charAt(i) != needle.charAt(j)) {
                if (j != 0) {
                    j = lps[j - 1]; // 回退到最长公共前后缀位置
                } else {
                    i++;
                }
            }
        }
        return -1;
    }
}

方法三:内置方法

复制代码
class Solution {
    public int strStr(String haystack, String needle) {
        return haystack.indexOf(needle);
    }
}

代码详解(暴力匹配示例 1 模拟)

示例 1:haystack = "sadbutsad", needle = "sad"

  • n = 9, m = 3,遍历 i 从 0 到 6:
    • i=0:比较 haystack[0] = 's'haystack[1] = 'a'haystack[2] = 'd',与 needle 完全匹配 → 返回 0

复杂度分析

方法 时间复杂度 空间复杂度 优点
暴力匹配 O(n*m) O(1) 代码简单,易理解
KMP 算法 O(n+m) O(m) 高效,适合大数据量
内置方法 O (n)(底层优化) O(1) 代码最简洁,面试推荐

总结

  1. 基础版:暴力匹配适合理解,代码简单,适合数据量小的场景。
  2. 进阶版:KMP 算法是字符串匹配的经典算法,核心是利用 LPS 数组避免重复比较,时间复杂度更优。
  3. 简洁版 :直接调用 indexOf(),在面试中是最快的写法,且底层已做优化。

这道题是字符串处理的入门题,掌握暴力匹配和 KMP 算法,能为后续学习更复杂的字符串算法打下基础。

今天的每日算法练习就到这里,我们明天再见!👋

相关推荐
酉鬼女又兒2 小时前
零基础快速入门前端深入掌握箭头函数、Promise 与 Fetch API —— 蓝桥杯 Web 考点全解析(可用于备赛蓝桥杯Web应用开发)
开发语言·前端·css·职场和发展·蓝桥杯·es6·js
阿Y加油吧3 小时前
力扣滑动窗口两大压轴题:最小覆盖子串 + 滑动窗口最大值(保姆级思路 + 代码详解)
算法·leetcode·职场和发展
北顾笙9803 小时前
day11-数据结构力扣
数据结构·算法·leetcode
月落归舟3 小时前
Lambda + Arrays---小练习
数据结构·算法
2601_955354463 小时前
seo臻系统和百度seo有什么区别
算法
君义_noip3 小时前
信息学奥赛一本通 1487:【例 2】北极通讯网络
算法·图论·信息学奥赛·csp-s
酉鬼女又兒3 小时前
零基础快速入门前端蓝桥杯 Web 备考:AJAX 与 XMLHttpRequest 核心知识点及实战(可用于备赛蓝桥杯Web应用开发)
前端·ajax·职场和发展·蓝桥杯·css3·js
June bug3 小时前
【AI赋能测试】基于 langchain+DeepSeek 的 AI 智能体
经验分享·功能测试·测试工具·职场和发展·langchain·自动化·学习方法
会编程的土豆3 小时前
【leetcode hot 100】二叉树二叉树
数据结构·算法·leetcode