最常见的想法肯定是枚举,枚举每个以某一个字符为起点的子数组,统计个数。优化的话可以考虑前缀和+哈希,用一个哈希表记录从i=0开始到前缀和为sj-k的个数,将每次统计的累积到ans上。
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int n=nums.size();
vector<int>s(n+1);
for(int i=0;i<n;i++){
s[i+1]=s[i]+nums[i];
}
int ans=0;
unordered_map<int,int>cnt;
for(int sj:s){
ans+=cnt.contains(sj-k)?cnt[sj-k]:0;
cnt[sj]++;
}
return ans;
}
};
我们发现,每次新要加入定长区间的元素,他只会影响比它小的元素,也就是说,所有比它小的元素在之后都不可能成为这个区间的最大值。所以我们用到一个单调递减的双端队列(单调队列),队头是该区间的最大值。首先要将前k个元素入队,按照单调递减的顺序。之后的元素一样按照此逻辑进行入队,不过当队头元素的下表不在区间范围内了,要进行出队。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int>q;
int n=nums.size();
vector<int>ans;
for(int i=0;i<k;++i){
while(!q.empty()&&nums[i]>=nums[q.back()]){
q.pop_back();
}
q.push_back(i);
}
ans.push_back(nums[q.front()]);
for(int i=k;i<n;++i){
while(!q.empty()&&nums[i]>=nums[q.back()]){
q.pop_back();
}
q.push_back(i);
while(q.front()<=i-k){
q.pop_front();
}
ans.push_back(nums[q.front()]);
}
return ans;
}
};
该题考到了滑动窗口,只需要不断维护满足条件的最短窗口即可,最后返回该长度的子串。
class Solution {
public:
int cnt[256];
string minWindow(string s, string t) {
int m=s.size(),n=t.size();
if(m<n)return "";
map<char,int>mp;
int nums=0;
for(auto &it :t){
if(mp.count(it)==0){
nums++;
}
mp[it]++;
}
int start=0,min_len=m+1;
for(int i=0,j=0;j<m;j++){
cnt[s[j]]++;
if(cnt[s[j]]==mp[s[j]]){
nums--;
}
while(cnt[s[i]]>mp[s[i]]){
cnt[s[i]]--;
i++;
}
if(nums==0&&j-i+1<min_len){
start=i;
min_len=j-i+1;
}
}
if(min_len==m+1)return "";
return s.substr(start,min_len);
}
};