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); //更新最大区间ansright++; //扩大窗口,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,转为tmapfor(int i=0;i < t.length();i++){char c = t.charAt(i); //获取字符ctmap.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; //滑动窗口右区间//再遍历swhile(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的keyint 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对应的子字符串即可。