26个字符,我复制怎么了?26个字符我比较个数怎么了? 顶多时间复杂度*26
本题用固定窗口大小的滑动窗口+每次比较包含26个元素的数组次数,最容易写。
动态窗口大小+哈希表存数值(双指针差值)难想难写。
一、动态滑动窗口+哈希表(双指针)
这个问题,刚开始想的是,维护一个滑动窗口,左指针left,右指针right,左指针往右走从集合中拿走这个字符,右指针往右走在集合中加入这个字符,但是由于p可能有多个重复字符,这使得我们不得不记录字符的个数了。那,我们记录个数的话,怎么记录呢?可以用哈希表存储该字符的个数,如果集合中加入一个字符,字符个数就减1,直至哈希表中没有元素则说明匹配成功,但是匹配了一次之后呢? 重新复制一次不得了,最多26个字符!
不过这里需要注意的是,当匹配成功后,左右指针都只能往后移动一次,只有当右指针遇到的字符不在目标字符串中时,才复制一次,完全重开。
这里的字符个数完全确定,最好使用vector<int>,查找更快。
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int left=0;
int right=0;
unordered_map<char,int> source;
for(auto &i:p) source[i]+=1;//可以复制,就26个字母,我复制怎么了?
vector<int> ans;
unordered_map<char,int> hmap(source);
while(right<s.size()){
if(hmap.find(s[right])!=hmap.end()){//在里面
hmap[s[right]]-=1;
if(hmap[s[right]]==0) hmap.erase(s[right]);
if(hmap.size()==0){
ans.emplace_back(left);
hmap[s[left++]]=1;
}
++right;
}else{
if(source.find(s[right])!=source.end()){//它在源头里面 可能有点用的
hmap[s[left++]]+=1;
}else {
hmap=source;//注意这里! 这里得还原了
left=++right;
}
}
}
return ans;
}
};
vector实现:
class Solution {
public:
vector<int> findAnagrams(string &s, string &p) {
if(s.size()<p.size()) return {};
vector<int> cnt_s(26);
vector<int> cnt_p(26);
vector<int> ans;
vector<int> zero(26);
for(char &i:p) ++cnt_p[i-'a'];
cnt_s=cnt_p;
int left=0,right=0;
while(right<s.size()){
if(cnt_s[s[right]-'a']>0){
--cnt_s[s[right]-'a'];
++right;
}else{
if(cnt_p[s[right]-'a']>0){
cnt_s[s[left]-'a']++;
++left;
}else{
cnt_s=cnt_p;
left=right=right+1;
}
}
if(cnt_s==zero) ans.push_back(left);
}
return ans;
}
};
二、固定滑动窗口
这里实际上就是上述方法用vector实现的。由于是26个字符,直接比较就行了。
class Solution {
public:
vector<int> findAnagrams(string &s, string &p) {
if(s.size()<p.size()) return {};
vector<int> source(26);
vector<int> hmap(26);
vector<int> ans;
for(int i=0;i<p.size();++i){
hmap[s[i]-'a']+=1;
source[p[i]-'a']+=1;
}
int left=0,right=p.size();
while(right<s.size()){
if(hmap == source) ans.emplace_back(left);
hmap[s[left++]-'a']-=1;
hmap[s[right++]-'a']+=1;
}
if(hmap == source) ans.emplace_back(left);
return ans;
}
};