题目描述
实现 strStr() 函数。给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从 0 开始)。如果不存在,则返回 -1。
说明:当 needle 是空字符串时,我们应当返回 0。这与 C 语言的strstr() 以及 Java 的 indexOf() 定义一致。
解题思路
核心思路:暴力匹配法
以字符串 haystack 的每一个字符为起点,逐一与字符串 needle 的字符进行对比。若从某个起点开始,能完整匹配 needle 的所有字符,则直接返回该起点索引;若遍历完haystack 仍无完整匹配,返回 -1。
复杂度分析
-
时间复杂度 :O((n-m) \\times m),其中 n 为
haystack长度,m 为needle长度。最坏情况下,需遍历haystack中 n-m+1 个起点,每个起点需对比 m 个字符。 -
空间复杂度:O(1),仅使用有限个变量存储索引和长度,不额外占用存储空间。
解题代码
java
class Solution {
public int strStr(String haystack, String needle) {
if ("".equals(needle)) {
return 0;
}
int len1 = haystack.length();
int len2 = needle.length();
int p = 0;
int q = 0;
while (p < len1) {
if (haystack.charAt(p) == needle.charAt(q)) {
if (len2 == 1) {
return p;
}
++p;
++q;
} else {
p -= q - 1;
q = 0;
}
if (q == len2) {
return p - q;
}
}
return -1;
}
}
代码解析
边界处理
首先判断 needle 是否为空字符串,若为空则直接返回 0,符合题目定义及主流语言的 API 行为。
指针逻辑
-
定义两个指针
p(遍历haystack)和q(遍历needle),初始均为 0。 -
当
haystack[p]与needle[q]匹配时,双指针同时加 1,继续对比下一个字符。 -
当字符不匹配时,
p回退到"上一轮匹配开始位置的下一位"(即p -= q - 1),q重置为 0,重新开始匹配。 -
当
q等于needle长度时,说明已完整匹配,此时匹配起点为p - q(因为p已遍历完匹配的所有字符),直接返回该索引。
特殊优化
当 needle 长度为 1 时,一旦找到匹配字符可直接返回当前 p,避免后续无意义的指针移动,小幅提升效率。
测试案例
案例 1:常规匹配
输入:haystack = "hello", needle = "ll" 输出:2 解析:从索引 2 开始,"ll" 与 haystack 子串完全匹配。
案例 2:无匹配项
输入:haystack = "aaaaa", needle = "bba" 输出:-1 解析:遍历完 haystack 无对应子串,返回 -1。
案例 3:needle 为空
输入:haystack = "anystring", needle = "" 输出:0 解析:空字符串按规则返回 0。
案例 4:needle 长度为 1
输入:haystack = "abc", needle = "c" 输出:2 解析:触发特殊优化,匹配后直接返回当前索引。
进阶思考
暴力匹配法思路简单、实现容易,但时间复杂度较高,在大规模字符串匹配场景下效率不佳。更高效的算法可参考 KMP 算法 ,通过预处理 needle 构建部分匹配表,将时间复杂度优化至 O(n+m),空间复杂度为 O(m)。后续将单独整理 KMP 算法的实现与解析。
总结
本题暴力匹配法是入门级解法,适合理解字符串匹配的核心逻辑,代码简洁且无额外空间开销,能应对 LeetCode 本题的所有测试用例。在实际开发中,若字符串规模较小,暴力法足够高效;若需处理大规模数据,建议采用 KMP、Boyer-Moore 等优化算法。