438. 找到字符串中所有字母异位词 - 力扣(LeetCode)
在写这两道题的时候,我觉得使用滑动窗口的思路会比较清晰,虽然这两题具体解法有差异,但是思考方式类似:
解法核心思想:
计数映射:用哈希表记录p中每个字符应该出现的次数
滑动验证:在s上滑动窗口,通过计数正负判断字符匹配情况
动态调整:当某个字符"超标"时,收缩左边界直到恢复正常
对于异位词这题具体说明一下滑窗的思考逻辑:

1. 确定窗口含义
-
固定窗口:窗口大小固定(如本题窗口大小固定为p的长度)
-
可变窗口:窗口大小根据条件变化(如前一个无重复字符问题)
2. 定义窗口状态
cpp
// 通常需要维护的状态
unordered_map<char, int> window; // 窗口内字符计数
int left = 0; // 窗口左边界
3. 明确扩展和收缩条件
cpp
for (int right = 0; right < s.size(); right++) {
// 1. 右指针扩展,更新窗口状态
window[s[right]]++;
// 2. 判断是否需要收缩左边界
while (需要收缩的条件) {
// 更新左边界对应的状态
window[s[left]]--;
left++;
}
// 3. 检查是否满足题目要求
if (满足条件) {
// 记录答案
}
}
完整题解代码:
cpp
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res;
unordered_map<char,int> cnt; // 记录p中字符出现次数
int s_len = s.size();
int p_len = p.size();
if(s_len < p_len) return {}; // 边界情况处理
// 初始化p的字符计数
for(char ch : p){
cnt[ch]++;
}
int left = 0;
for(int right = 0; right < s_len; right++){
cnt[s[right]]--; // 右指针扩展,对应字符计数减1
// 关键:当某个字符计数为负,说明当前窗口包含过多该字符
while(cnt[s[right]] < 0){
cnt[s[left]]++; // 左指针收缩,恢复字符计数
left++;
}
// 窗口大小等于p长度时,找到异位词
if(right - left + 1 == p_len){
res.push_back(left);
}
}
return res;
}
};
在遇到滑动窗口问题时,我觉得可以从以下几个方面去思考:
-
窗口大小是固定还是可变的?
-
需要记录哪些窗口状态?(字符计数、和、乘积等)
-
右指针扩展时如何更新状态?
-
什么条件下需要收缩左边界?
-
如何判断当前窗口是否满足要求?
-
边界情况如何处理?