分析
要找到p的所有字母异位词,其实是找到字符串p里各种字母的组合。那么我们可以通过维护len(p)长度的窗口来判断是否是p的字母异位词。
题解
滑动窗口
固定长度滑动窗口,窗口大小等于p.size()。
- 用两个大小26的数组,分别统计
p、s窗口内每个字母出现次数; - 数组完全相等 = 窗口内字符是
p的字符异位词; - 每次窗口右移一格:左边出去一个字符计数减一,右边进来一个字符计数加一。
代码
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int sLen = s.size(), pLen = p.size();
if(sLen < pLen) return {};
vector<int> ans;
vector<int> sCount(26);
vector<int> pCount(26);
for(int i = 0; i < pLen; i++) {
pCount[p[i] - 'a']++;
sCount[s[i] - 'a']++;
}
if(sCount == pCount) ans.emplace_back(0);
for(int i = 0; i < sLen - pLen; i++) {
sCount[s[i] - 'a']--;
sCount[s[i + pLen] - 'a']++;
if(sCount == pCount) {
ans.emplace_back(i + 1);
}
}
return ans;
}
};
- 时间复杂度: O ( n ) O(n) O(n),n为
s长度,数组比较是固定26次,属于常数开销- 空间复杂度: O ( 1 ) O(1) O(1),两个26大小数组,与输入规模无关
优化的滑动窗口
核心思想:只用一个 cnt[26] 数组
p的字符:cnt++s窗口内字符:cnt--
如果窗口是p的异位词,则全部 26 个位置cnt[i] = 0;
diff记录不等于 0 的字母个数,diff == 0代表窗口匹配成功。
代码
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int sLen = s.size(), pLen = p.size();
if(sLen < pLen) return {};
vector<int> cnt(26,0);
vector<int> ans;
for(int i = 0; i < pLen; i++){
cnt[p[i]-'a']++;
cnt[s[i]-'a']--;
}
int diff = 0;
for(int x: cnt) if(x!=0) diff++;
if(diff == 0) ans.push_back(0);
for(int i = 0; i < sLen - pLen; i++){
int out = s[i]-'a';
// 移动之前,如果cnt[out]==0:说明这个字母之前两边完全相等
// 现在要修改它,相等状态被破坏,diff+1
if(cnt[out] == 0) diff++;
cnt[out]++; // s窗口去掉这个字符,所以差值数组+1
// 修改完之后,如果cnt[out]==0:现在两边又相等了,diff‑1
if(cnt[out] == 0) diff--;
int in = s[i + pLen]-'a';
// 修改前等于0,原本相等,即将被破坏,diff++
if(cnt[in] == 0) diff++;
cnt[in]--; // s窗口新增该字符,差值数组-1
// 修改后等于0,该字母计数达成一致,diff--
if(cnt[in] == 0) diff--;
// diff=0,全部字母计数匹配,窗口起始位置 i+1
if(diff == 0) ans.push_back(i+1);
}
return ans;
}
};
- 时间复杂度: O ( n + m ) O(n + m) O(n+m),n是
s长度,m是p长度;不再每次循环比较26个 vector 元素- 空间复杂度: O ( 1 ) O(1) O(1),固定大小 26 数组。
对比原始双 vector 写法:原始写法每次if(sCount == pCount)底层会循环比较 26 次,虽然是常数,但 diff 版本把 26 次比较消除,效率更高。

4130

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



