每日算法练习: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 算法,能为后续学习更复杂的字符串算法打下基础。

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

相关推荐
洛水水3 小时前
【力扣100题】18.随机链表的复制
算法·leetcode·链表
南宫萧幕3 小时前
规则基 EMS 仿真实战:SOC 区间划分与 Simulink 闭环建模全解
算法·matlab·控制
多加点辣也没关系3 小时前
数据结构与算法|第二十三章:高级数据结构
数据结构·算法
hoiii1876 小时前
孤立森林 (Isolation Forest) 快速异常检测系统
算法
c++之路7 小时前
适配器模式(Adapter Pattern)
java·算法·适配器模式
吴声子夜歌7 小时前
Java——接口的细节
java·开发语言·算法
myheartgo-on7 小时前
Java—方 法
java·开发语言·算法·青少年编程
宝贝儿好8 小时前
【LLM】第三章:项目实操案例:智能输入法项目
人工智能·python·深度学习·算法·机器人
雪碧聊技术9 小时前
上午题_算法
算法·软考·软件设计师