一、题目
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/find-all-anagrams-in-a-string/description/
二、C++解法
我的思路及代码
滑动窗口
采用 left 和 right 来维护窗口当前的大小,两个 map 为 need 和 win 分别维护当前字串中所含有的各个字符的个数和当前窗口中含有目标字符的个数,valid 来维护当前窗口内满足需求的字符个数。总体思路为,先初始化窗口内的目标字符数量,然后同步扩大 left 和 right ,当 valid = need 的大小时,就说明此时的子串已经满足了需求,在ans中添加当前的 left 下标,当 while 结束后说明不能满足条件,返回 ans 即可。
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int left,right;
int valid = 0;
left = 0;
right = p.size();
vector<int> ans;
unordered_map<char,int> need,win;
//初始化need
for(char c:p)
need[c]++;
//初始化win
for(int i=0;i<p.size();i++){
if(need.count(s[i])){
win[s[i]]++;
if(need[s[i]] == win[s[i]])
valid++;
}
}
while(right<s.size()){
if(valid == need.size())
ans.push_back(left);
char c = s[right];
right++;
if(need.count(c)){
win[c]++;
if(need[c] == win[c])
valid++;
}
c = s[left];
left++;
if(need.count(c)){
if(need[c] == win[c])
valid--;
win[c]--;
}
}
if(valid == need.size())
ans.push_back(left);
return ans;
}
};
- 时间复杂度:O(n+m),其中 n 为字符串 s 的长度,m 为字符串 p 的长度。我们需要 O(m) 来统计字符串 p 中每种字母的数量;需要 O(m) 来初始化滑动窗口;需要 O(n−m) 来滑动窗口并判断窗口内每种字母的数量是否与字符串 p 中每种字母的数量相同,每次判断需要 O(1)
- 空间复杂度:O(m),和字符串p的长度有关系
官方参考代码
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int sLen = s.size(), pLen = p.size();
if (sLen < pLen) {
return vector<int>();
}
vector<int> ans;
vector<int> count(26);
for (int i = 0; i < pLen; ++i) {
++count[s[i] - 'a'];
--count[p[i] - 'a'];
}
int differ = 0;
for (int j = 0; j < 26; ++j) {
if (count[j] != 0) {
++differ;
}
}
if (differ == 0) {
ans.emplace_back(0);
}
for (int i = 0; i < sLen - pLen; ++i) {
if (count[s[i] - 'a'] == 1) { // 窗口中字母 s[i] 的数量与字符串 p 中的数量从不同变得相同
--differ;
} else if (count[s[i] - 'a'] == 0) { // 窗口中字母 s[i] 的数量与字符串 p 中的数量从相同变得不同
++differ;
}
--count[s[i] - 'a'];
if (count[s[i + pLen] - 'a'] == -1) { // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从不同变得相同
--differ;
} else if (count[s[i + pLen] - 'a'] == 0) { // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从相同变得不同
++differ;
}
++count[s[i + pLen] - 'a'];
if (differ == 0) {
ans.emplace_back(i + 1);
}
}
return ans;
}
};
- 时间复杂度:O(n+m+Σ),其中 n 为字符串 s 的长度,m 为字符串 p 的长度,其中Σ 为所有可能的字符数。我们需要 O(m) 来统计字符串 p 中每种字母的数量;需要 O(m) 来初始化滑动窗口;需要 O(Σ) 来初始化 differ;需要 O(n−m) 来滑动窗口并判断窗口内每种字母的数量是否与字符串 p 中每种字母的数量相同,每次判断需要 O(1)。因为 s 和 p 仅包含小写字母,所以 Σ=26
- 空间复杂度:O(Σ)。用于存储滑动窗口和字符串 p 中每种字母数量的差