💡 【LeetCode Hot100】找到字符串中所有字母异位词 ,滑动窗口+数组,Java实现!图解+代码,小白也能秒懂!
✏️ 题目链接:找到字符串中所有字母异位词
📌 题目描述
给定两个字符串 s 和 p,找到 s 中所有 p 的 字母异位词 的子串,返回这些子串的起始索引。
字母异位词:由相同字母重排列形成的字符串(包括相同的字符串)。
示例:
输入:s = "cbaebabacd", p = "abc"
输出:[0, 6]
解释:
起始索引 0 的子串 "cba" 是 "abc" 的字母异位词。
起始索引 6 的子串 "bac" 是 "abc" 的字母异位词。
🧠 解题思路
核心问题
如何在 s 中高效找到所有满足条件的子串?
方法:滑动窗口 + 数组统计法
- 初始化:
- 使用两个长度为 26 的数组
pCnt和sCnt,分别统计p和当前窗口中每个字符的出现次数。
- 使用两个长度为 26 的数组
- 统计字符频率:
- 遍历
p和s的前m个字符,初始化pCnt和sCnt。
- 遍历
- 滑动窗口:
- 从
m开始遍历s,每次移动窗口:- 移除左边界字符,更新
sCnt。 - 添加右边界字符,更新
sCnt。 - 如果
sCnt和pCnt相等,说明当前窗口是p的字母异位词,记录起始索引。
- 移除左边界字符,更新
- 从
- 返回结果:所有满足条件的子串起始索引。
🚀 代码实现
public static List<Integer> findAnagrams2(String s, String p) {
int n = s.length(), m = p.length();
List<Integer> res = new ArrayList<>();
// 如果 s 的长度小于 p 的长度,直接返回空列表
if (n < m) return res;
// 初始化统计数组
int[] pCnt = new int[26]; // 统计 p 中每个字符的频率
int[] sCnt = new int[26]; // 统计当前窗口中每个字符的频率
// 遍历 p 和 s 的前 m 个字符,初始化 pCnt 和 sCnt
for (int i = 0; i < m; i++) {
pCnt[p.charAt(i) - 'a']++; // 统计 p 中字符的频率
sCnt[s.charAt(i) - 'a']++; // 统计 s 中前 m 个字符的频率
}
// 如果初始窗口满足条件,记录起始索引 0
if (Arrays.equals(sCnt, pCnt)) {
res.add(0);
}
// 滑动窗口遍历
for (int i = m; i < n; i++) {
// 移除左边界字符
sCnt[s.charAt(i - m) - 'a']--;
// 添加右边界字符
sCnt[s.charAt(i) - 'a']++;
// 如果当前窗口满足条件,记录起始索引
if (Arrays.equals(sCnt, pCnt)) {
res.add(i - m + 1);
}
}
return res;
}
🎨 图解滑动窗口
示例:
s = "cbaebabacd"
p = "abc"
步骤1:初始化
pCnt = [1, 1, 1, 0, 0, ...](a:1,b:1,c:1)sCnt = [1, 1, 1, 0, 0, ...](c:1,b:1,a:1)res = [0](初始窗口满足条件)
步骤2:滑动窗口
- 窗口右移:
- 移除
c,添加e→sCnt = [1, 1, 0, 0, 1, ...] - 移除
b,添加b→sCnt = [1, 1, 0, 0, 1, ...] - 移除
a,添加a→sCnt = [1, 1, 0, 0, 1, ...] - 移除
e,添加b→sCnt = [1, 2, 0, 0, 0, ...] - 移除
b,添加a→sCnt = [2, 1, 0, 0, 0, ...] - 移除
a,添加c→sCnt = [1, 1, 1, 0, 0, ...](满足条件)- 添加索引
6到res。
- 添加索引
- 移除
最终结果:
res = [0, 6]
💡 复杂度分析
- 时间复杂度:O(n)
- 只需遍历字符串一次。
- 空间复杂度:O(1)
- 使用固定大小的数组。
🌟 总结
- 核心思想:通过滑动窗口动态维护字符频率,利用数组快速比较窗口是否满足条件。
- 适用场景:字符串匹配、子串问题。
1380

被折叠的 条评论
为什么被折叠?



