给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
提示:
1 <= target <= 10^9
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^5
本题是滑动窗口
先移动右边指针(即末尾元素),直到sum >= target;然后再移动左边指针,取最小长度。
【整理时和动态规划那几道求长度的题目对比一下区别】
/**
* @param {number} target
* @param {number[]} nums
* @return {number}
*/
var minSubArrayLen = function(target, nums) {
let res = Infinity;
let left = 0;
let sum = 0;
for (let right = 0;right < nums.length;right++){
sum += nums[right];
while (sum >= target){
res = Math.min(res,right - left + 1);
sum -= nums[left];
left++;
}
}
return res == Infinity ? 0 : res;
};
不要光靠回忆做题。要认真审题,抓住关键点,认真分析。
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 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] 这五棵树。
提示:
1 <= fruits.length <= 10^5
0 <= fruits[i] < fruits.length
我们可以使用滑动窗口解决本题,left 和 right 分别表示满足要求的窗口的左右边界,同时我们使用哈希表存储这个窗口内的数以及出现的次数。
我们每次将 right 移动一个位置,并将 fruits[right] 加入哈希表。如果此时哈希表不满足要求(即哈希表中出现超过两个键值对),那么我们需要不断移动 left,并将 fruits[left] 从哈希表中移除,直到哈希表满足要求为止。
需要注意的是,将fruits[left] 从哈希表中移除后,如果 fruits[left] 在哈希表中的出现次数减少为 0,需要将对应的键值对从哈希表中移除。
作者:LeetCode-Solution
/**
* @param {number[]} fruits
* @return {number}
*/
var totalFruit = function(fruits) {
let left = 0,res = 0;
let cnt = new Map();
for (let right = 0;right < fruits.length;right++){
cnt.set(fruits[right],(cnt.get(fruits[right]) || 0) + 1);
while (cnt.size > 2){
cnt.set(fruits[left],cnt.get(fruits[left]) - 1);
if (cnt.get(fruits[left]) == 0){
cnt.delete(fruits[left]);
}
left++
}
res = Math.max(res,right - left + 1);
}
return res;
};
不算难,主要自己对JS的数据结构不熟悉
Map的数据结构
存入数据:map.set(a,b);
获取数据:map.get(a) || 0 ;
在map中搜索元素a,若能找到则返回value,若找不到则返回undefined
删除数据:map.delet(a);
数据的长度:map.size 【没有括号】
时间复杂度:O(n),其中 n 是数组 fruits 的长度。
空间复杂度:O(1)。哈希表中最多会有三个键值对,可以看成使用了常数级别的空间。
给你一个字符串 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 的子串中,
因此没有符合条件的子字符串,返回空字符串。
提示:
m == s.length
n == t.length
1 <= m, n <= 10^5
s 和 t 由英文字母组成
本问题要求我们返回字符串 s 中包含字符串 t 的全部字符的最小窗口。我们称包含 t 的全部字母的窗口为「可行」窗口。
我们可以用滑动窗口的思想解决这个问题。在滑动窗口类型的问题中都会有两个指针,一个用于「延伸」现有窗口的 r 指针,和一个用于「收缩」窗口的 l指针。在任意时刻,只有一个指针运动,而另一个保持静止。我们在 s上滑动窗口,通过移动 r指针不断扩张窗口。当窗口包含 t 全部所需的字符后,如果能收缩,我们就收缩窗口直到得到最小窗口。
本题目的关键在于如何确定r指针扩张的窗口正好包含t全部所需字符
本题建立两个哈希表,抽取一个函数用来比较r指针移动下的哈希表是否能够覆盖t字符串的哈希表
作者:LeetCode-Solution
Java
public String minWindow(String s, String t) {
HashMap<Character, Integer> map1 = new HashMap<>();
HashMap<Character, Integer> map2 = new HashMap<>();
for (int i = 0; i < t.length(); i++) {
map2.put(t.charAt(i), map2.getOrDefault(t.charAt(i), 0) + 1);
}
int left = 0;
int minLength = Integer.MAX_VALUE;
int key1 = 0, key2 = 0;
for (int right = 0; right < s.length(); right++) {
map1.put(s.charAt(right), map1.getOrDefault(s.charAt(right), 0) + 1);
// 判断覆盖是否成功
boolean judgeCover = judgeCover(map1, map2);
// 一旦成功 试图进行滑动窗口缩减
while (judgeCover) {
if (minLength > right - left + 1) {
minLength = right - left + 1;
key1 = left;
key2 = right;
}
// 缩减map中的left字符,然后判断字符数量是否等于0,如果等于0就移除
map1.put(s.charAt(left), map1.get(s.charAt(left)) - 1);
if (map1.get(s.charAt(left)) == 0) {
map1.remove(s.charAt(left));
}
left++;
// 每次缩减都去重新判断是否覆盖 如果覆盖失败,则继续right++,left不动
judgeCover = judgeCover(map1, map2);
}
}
return minLength == Integer.MAX_VALUE ? "" : s.substring(key1, key2 + 1);
}
/**
* 每次去判断是否已经产生覆盖的情况
* @param map1
* @param map2
* @return
*/
public boolean judgeCover(HashMap<Character, Integer> map1, HashMap<Character, Integer> map2) {
Set<Map.Entry<Character, Integer>> entries = map2.entrySet();
for (Map.Entry<Character, Integer> entry : entries) {
Character key = entry.getKey();
if (!map1.containsKey(key)) {
return false;
} else {
if (entry.getValue() > map1.get(key)) {
return false;
}
}
}
return true;
}
Javascript
/**
* @param {string} s
* @param {string} t
* @return {string}
*/
var minWindow = function(s, t) {
let sMap = new Map();
let tMap = new Map();
let key1 = 0,key2 = 0;
let minLenth = Infinity;
// 用来缩小范围的指针
let left = 0;
for (let i = 0;i < t.length;i++){
tMap.set(t[i],(tMap.get(t[i]) || 0) + 1);
}
for (let right = 0;right < s.length;right++){
sMap.set(s[right],(sMap.get(s[right]) || 0) + 1);
while (judgetCover(sMap,tMap)){
// 目前已加入的 s 能够覆盖 t 字符串进行如下操作
if (minLenth > right - left + 1){
minLenth = right - left + 1;
key1 = left;
key2 = right;
}
sMap.set(s[left],sMap.get(s[left]) - 1);
if (sMap.get(s[left]) == 0){
sMap.delete(s[left]);
}
left++;
}
}
return minLenth == Infinity ? "" : s.substring(key1,key2+1);
};
// forEach一直报错(不太懂),改成for...of 好了
var judgetCover = function(sMap, tMap){
// tMap.forEach(function(value,key){
// if (!sMap.has(key)){
// return false;
// } else {
// if (value > sMap.get(key)){
// return false;
// }
// }
// });
// return true;
for (let x of tMap){
if (!sMap.has(x[0])){
return false;
} else {
if (x[1] > sMap.get(x[0])){
return false;
}
}
}
return true;
};