11.7滑动窗口
11/7
每日一题:长度为k的子数组的能量值2
题目跟昨天是一样的,只是复杂度要求不同,昨天的做法依旧能过
class Solution {
public:
vector<int> resultsArray(vector<int>& nums, int k) {
int n = nums.size();
vector<int> res(n - k + 1 , -1);
int cnt = 0;
for (int i = 0; i < n; i++) {
if(i == 0 || nums[i] - nums[i - 1] == 1) cnt ++;
else cnt = 1;
if(cnt >= k){
res[i - k + 1] = nums[i];
}
}
return res;
}
};
209.长度最小的子数组
NOT AC
思路:时间复杂度O(n)的做法:滑动窗口
常规方法每次确定子数组的开始下标,然后得到长度最小的子数组,因此时间复杂度较高。为了降低时间复杂度,可以使用滑动窗口的方法。
定义两个指针
start和end分别表示子数组(滑动窗口窗口)的开始位置和结束位置,维护变量sum存储子数组中的元素和(即从nums[start]到nums[end]的元素和)。每一轮迭代,将
nums[end]加到sum,如果sum≥s,则更新子数组的最小长度(此时子数组的长度是end−start+1),然后将nums[start]从sum中减去并将start右移,直到sum<s,在此过程中同样更新子数组的最小长度。在每一轮迭代的最后,将end右移。复杂度解释:
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作1次,出去操作1次,每个元素都是被操作两次,所以时间复杂度是 2 * n 也就是O(n)。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
int start = 0;
int sum = 0;
int res = 0x3f3f3f3f;
for (int end = 0 ; end < n ; end ++){
sum += nums[end];
while(sum >= target){
res = min(res , end - start + 1);
sum -= nums[start ++];
}
}
return res == 0x3f3f3f3f? 0 : res;
}
};
M:904.水果成篮
思路:无序哈希表+滑动窗口
同类的水果要把数量合并,因此考虑用无序图哈希表,注意定义方法是
unordered_map<int , int> mp; //加fruits[end]类型的水果数量 mp[fruits[end]] ++;当哈希表size>2时,用迭代器It找到窗口最左边的值fruits[start],将它的数量减一,直到减至0就删除(因为fruits数组可能出现 [ 1 , 2 , 1 , 2 ,1 ,2···]的波动数列),每轮将窗口起始位置右移。在满足mp.size() <= 2的每个循环末尾更新res为最大值。
class Solution {
public:
int totalFruit(vector<int>& fruits) {
unordered_map<int , int> mp;
int res = 0;
int start = 0;
for (int end = 0 ; end < fruits.size() ; end ++){
mp[fruits[end]] ++;
while(mp.size() > 2){
auto it = mp.find(fruits[start]);
-- it->second;
if(it->second == 0) mp.erase(it);
start ++;
}
res = max(res , end - start + 1);
}
return res;
}
};
H:76.最小覆盖子串(opens new window)
思路:哈希表+滑动窗口
定义两个哈希表,ss存放滑动窗口中元素出现次数,tt存放t中元素出现次数,tt可直接根据t初始化。
定义一个检查当前窗口是否合格的函数check(),要求tt中元素出现的次数小于等于ss中元素出现次数。
bool check(){ for (auto it : tt){ if(ss[it.first] < it.second) return false; } return true; }边界条件:
如果s.size() < t.size() , 直接输出空串“”;
如果每次r指针右移更新进来的都是没用的字母,即在tt里找不到,最后也是输出空串。如样例
s = "a" , t = "b"因此需要在每次r指针右移后加入判断条件,判断加进来的元素是否在tt里面出现过,出现过了再以check()判断子串是否合格,并更新子数组长度len,以及答案的左边界ans_l,如果从始至终没有更新过ans_l(如果更新则至少为l的初值0),则说明输入的都是没用的字母。
for(int r = 0 ; r < s.size() ; r ++){ ss[s[r]] ++; if(tt.find(s[r]) != tt.end()){ //tt里有s[r],更新有效 while(check() && l <= r){ //满足题意,则更新子数组边界和长度 if(len > r - l + 1){ len = r - l + 1; ans_l = l } ss[s[l]] --;//如果当前子串合格,则尝试右移左边界。 l ++; } } }最后输出时可以直接用
substr函数截取返回,也是为什么计算ans_l,答案长度len的原因return s.substr(ans_l , len);
class Solution {
public:
unordered_map<char , int> ss;
unordered_map<char , int> tt;
bool check(){
for (auto it: tt){
if(ss[it.first] < it.second) return false;
}
return true;
}
string minWindow(string s, string t) {
if(s.size() < t.size() ) return "";
for (char ch : t) tt[ch] ++;
int l = 0;
int ans_l = -1;
int len = 0x3f3f3f3f;
for (int r = 0 ; r < s.size() ; r ++){
ss[s[r]] ++;
if(tt.find(s[r]) != tt.end()){
while(check() && l <= r){
if(len > r - l + 1){
len = r - l + 1;
ans_l = l;
}
auto it = ss.find(s[l]);
-- it->second;
if(it->second == 0) ss.erase(it);
l ++;
}
}
}
if(ans_l == -1) return "";
return s.substr(ans_l , len);
}
};
11.盛最多水的容器
思路:
双指针定义左右指针l = 0 , r = n - 1,由于每次都是移一位,让高度小的移动能使value尽可能大。
class Solution {
public:
int maxArea(vector<int>& height) {
int n = height.size();
int res = 0;
int value = 0;
for ( int l = 0, r = n - 1 ; l < r; ){
value = (r - l) * min(height[l] , height[r]);
if(value > res) res = value;
if(height[l] > height[r]) r --;
else l ++;
}
return res;
}
};
3.无重复字符的最长子串
本题与76.最小覆盖子串类似的做法。区别是本题求最大子串长度,因此在不满足
check()时才右移左边界,在满足时可以一直右移右边界。先定义
check()函数,字母个数大于1的时候返回false每次窗口右边界右移进来新元素的时候
check(),满足则更新答案res;当不满足的时候试着把左边界右移,直到符合为止。
class Solution {
public:
unordered_map<char , int> ss;
bool check(){
for (auto it : ss){
if(it.second > 1) return false;
}
return true;
}
int lengthOfLongestSubstring(string s) {
int l = 0;
int res = 0;
for (int r = 0 ; r < s.size() ; r ++){
ss[s[r]] ++;
if(check() && l <= r){
res = max(res , r - l + 1);
}
while(!check()){
ss[s[l]] --;
l ++;
}
}
return res;
}
};

浙公网安备 33010602011771号