代码随想录:长度最小的子数组相关题目推荐(904、76)

904.水果成篮

题目

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

  • 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
  • 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
  • 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。

给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

示例 1:

输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。

示例 2:

输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

示例 3:

输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。

示例 4:

输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。

代码(哈希表)

class Solution {
    public int totalFruit(int[] fruits) {
        HashMap<Integer,Integer> map = new HashMap<>();
        int left = 0;  //滑动窗口左区间
        int right = 0;  //滑动窗口右区间
        int ans = -1;  //长度
        //right遍历数组
        while(right < fruits.length){
            //把right加入窗口(right可能不存在)
            map.put(fruits[right],map.getOrDefault(fruits[right],0)+1);
            //只要水果种类大于2,不断缩小窗口,左区间右移
            while(map.size() > 2){
                //left右移
                map.put(fruits[left],map.get(fruits[left])-1); //left的key对应的value减1
                //如果left长度为0,从hash表中删除
                if(map.get(fruits[left]) == 0){
                    map.remove(fruits[left]);  //根据key移除left种类
                }
                left++;  //left已经从滑动窗口里删除了,left++
            }
            ans = Math.max(ans,right-left+1); //更新最大区间ans
            right++;  //扩大窗口,right右移继续遍历
        }
    return ans;
    }
}

总结

        题目看着很绕,其实就要找一个最大的连续数组(数字种类小于等于2)而已。

        查找方法就是用滑动窗口,left和right固定。

        然后,right遍历水果数组,把right种类put到哈希表中(key是水果种类,value是出现次数)。map存储的是当前left和right窗口中的水果种类和对应的数量。

        如果map的大小>2,即水果种类大于2,就要缩小窗口,修改map,map中key为left的value减1。如果value为0,直接把left从map中remove。然后,left右移,直到size小于2。

        最后,更新最大长度ans结果,right++继续遍历。

76.最小覆盖子串

题目

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

注意:

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

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

示例 2:

输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

代码(哈希表)

class Solution {

    //tmap保存t字符串对应的哈希表
    HashMap<Character,Integer> tmap = new HashMap<>();
    //smap保存s字符串对应的哈希表,保存滑动窗口的字符串哈希表
    HashMap<Character,Integer> smap = new HashMap<>();

    public String minWindow(String s, String t) {
        //先遍历t,转为tmap
        for(int i=0;i < t.length();i++){
            char c = t.charAt(i);  //获取字符c
            tmap.put(c,tmap.getOrDefault(c,0)+1); //把c加入tmap中
        }

        int minlen = Integer.MAX_VALUE;  //最小长度
        int minleft = -1;  //minlen对应的左指针
        int minright = -1;  //minlen对应的右指针
        int left = 0;  //滑动窗口左区间
        int right = 0;  //滑动窗口右区间
        //再遍历s
        while(right < s.length()){
            char c = s.charAt(right);  //获取s中的right字符
            smap.put(c,smap.getOrDefault(c,0)+1);
            
            //判断smap是不是>=tmap,即smap是否可行
            while(check()){  //smap可行
                int length = right - left + 1;  //计算当前窗口大小
                //更新最小窗口大小和对应的指针
                if(length < minlen){
                    minlen = length;
                    minleft = left;
                    minright = right;
                }
                //当前可行窗口以及记录好了,要缩小区间了
                char ch = s.charAt(left);
                smap.put(ch,smap.get(ch)-1);
                //这个if不写也行
                if(smap.get(ch) == 0){
                    smap.remove(ch);  
                }
                left++;
            }
            right++;  //窗口扩大,right右移
        }
        return minlen == Integer.MAX_VALUE ? "" : s.substring(minleft,minright+1);
    }
    //只有smap中的key和count都满足了,smap才是可行哈希表
    public boolean check(){
        //遍历tmap,判断每个key对应的value,再smap中是否够了
        for(Map.Entry<Character,Integer> entry : tmap.entrySet()){
            char key = entry.getKey();  //获取tmap的key
            int value = entry.getValue();  //获取tmap的value
            //看看smap中是否存在这个key,且value值要>=tmap中的
            if(smap.getOrDefault(key,0) < value){
                return false; //说明这个entry的smap就已经不可行了
            }
        }
        return true; //tmap中每个entry,smap都满足,说明smap可行
    }
}

总结

        先用tmap存储t字符串对应的keyvalue哈希表。然后用smap存储s字符串滑动窗口对应的哈希表。用滑动窗口遍历smap,用left和right固定。

        先把当前right字符put到smap中。然后循环判断smap是否满足可行要求。

        如果不满足,继续扩大窗口,right右移。

        如果满足,就要更新minlen最小长度以及对应的区间指针,同时,把smap的entry进行缩小修改。

        最后,当right遍历完s字符串后,返回minleft和minright对应的子字符串即可。

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

守岁白驹hh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值