滑动窗口算法练习

leetcode-904 水果成篮

题目描述

在一排树中,第 i 棵树产生 tree[i] 型的水果。
你可以从你选择的任何树开始,然后重复执行以下步骤:

把这棵树上的水果放进你的篮子里。如果你做不到,就停下来。
移动到当前树右侧的下一棵树。如果右边没有树,就停下来。
请注意,在选择一颗树后,你没有任何选择:你必须执行步骤 1,然后执行步骤 2,然后返回步骤 1,然后执行步骤 2,依此类推,直至停止。

你有两个篮子,每个篮子可以携带任何数量的水果,但你希望每个篮子只携带一种类型的水果。

用这个程序你能收集的水果树的最大总量是多少?

示例 1:
输入:[1,2,1]
输出:3
解释:我们可以收集 [1,2,1]。

示例 2:
输入:[0,1,2,2]
输出:3
解释:我们可以收集 [1,2,2]
如果我们从第一棵树开始,我们将只能收集到 [0, 1]。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fruit-into-baskets

思路

我们可以创建两个指针left和right来标记我们收集到的果树的范围(如left=0,right=3那么我们能收集到的果树就是3-0+1=4棵),假如我们先选任意一颗树作为我们要收集的第一棵树(后面再说如何选择),因为这肯定是一颗全新的果树,所以我们需要一个果篮来专门收集这颗树上的水果,我们可以创建一个a使其等于left指针指向的值。然后另right以left为起点,开始向右移动,遇到第一个与a不同的值时停下(因为此时出现了一个新的果树),同理创建一个b使其等于right指针指向的值,然后令right继续向右移动,在right+1!=a&&right+1!=b处停下(当然也要注意是否会越界),因为此时又遇到了一颗新果树,而且已经没有果篮可以装新的水果了,然后再计算这一次收集了几颗果树。

至于如何选择left的值,毫无意外left一开始肯定在坐标零处,在每一次收集结束时,令left=right,如果左边有相同元素再令left一直左移。
因为按照规则 [left,right] 中间只能有两种水果,而且right+1肯定是第三种水果,所以我们要是继续执着于收集这两种水果,后续的范围一定必刚刚那中情况小,所以我们得舍弃一种水果,由于我们是从左向右遍历,所以只能保持最后遇见的那种水果,于是就有了上述选择方法。

代码

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int maxNum=0;
        int left=0;
        int right=0;
        while(right<fruits.size()){            
            while(fruits[right]==fruits[left]&&right+1<fruits.size()){ //要注意越界问题
                right++;
            }
            int a=fruits[left];
            int b=fruits[right];
            while(1){
                if(right+1>=fruits.size()||fruits[right+1]!=a&&fruits[right+1]!=b){//同上
                    break;
                }
                right++;
            }
            maxNum=max(maxNum,right-left+1);
            if(right==fruits.size()-1) break; //只剩一种水果了,溜了
            left=right;
            while(fruits[left-1]==fruits[left]){
                left--;
            }
        }
        return maxNum;
    }
};

leetcode-76 最小覆盖子串

题目描述

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。

注意:

对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”

示例 2:
输入:s = “a”, t = “a”
输出:“a”

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-window-substring

思路

我们可以创建两个map,ht用于存储t中各个元素的数量,hs用于存储我们构建的“窗口”中各个元素数量,构建一个n=hs.size(),每当hs中键的值=ht中键的值时,n–,当n==0时,说明该窗口包括的字符串是一个符合要求的子串。
关于窗口:构建l和r都位于字符串起点,从字符串第一个字符开始判断是否属于字符串t,如果属于,则对应的hs的值++,并判断对应的hs是否等于对应的ht,如果相等,则n–,并判断n是否为零,等于零,说明找到一个符合条件的子串,然后做相应处理。
上述情况,都是关于指针r的移动,而关于指针l的移动,应在找到一个符合条件的子串之后,令l右移直至n>0。
简单来说,指针r一直右移直至找到刚好符合条件的点,然后再右移指针l直至找到刚好破坏条件的点,处理字符串则在每一次右移指针l之前。

代码

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char,int> ht,hs;
        for(auto c:t) ht[c]++;	
        string res;	//用于存储结果
        int cnt=0;	//用于计数已经有几个符合条件的位
        int n=ht.size();	//总共有多少需求。。
        int l=0,r=0;
        while(r<s.size()&&ht.find(s[l])==ht.end()){	//提高一点效率,没用的就不管了
            l++;
            r++;
        }
        while(r<s.size()){
            if(++hs[s[r]]==ht[s[r]]){//注意不能是>=,因为可能存在多个重复的元素
                cnt++;
            }
            while(l<s.size()&&cnt==n){//找到破环条件的点之前一直循环
                if(res.empty()||res.size()>(r-l+1)){
                    res=s.substr(l,r-l+1);
                }
                while(l<s.size()&&ht.find(s[l])==ht.end()){
                    l++;
                }
                if(--hs[s[l]]<ht[s[l]]){
                    cnt--;
                }
                l++;
            }
            r++;
        }
        return res;
    }
    
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值