给定一个含有 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
解法:滑动窗口
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int n = nums.length;
int ans = n + 1;
int sum = 0; // 子数组元素和
int left = 0; // 子数组左端点
for (int right = 0; right < n; right++) { // 枚举子数组右端点
sum += nums[right];
while (sum - nums[left] >= target) { // 尽量缩小子数组长度
sum -= nums[left++]; // 左端点右移
}
if (sum >= target) {
ans = Math.min(ans, right - left + 1);
}
}
return ans <= n ? ans : 0;
}
}
给定一个字符串 s
,请你找出其中不含有重复字符的 最长 子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc"
,所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b"
,所以其长度为 1。
解法一:滑动窗口
class Solution {
public int lengthOfLongestSubstring(String s) {
if (s.length()==0) return 0;
HashMap map = new HashMap();
int max = 0;
int left = 0;
for(int i = 0; i < s.length(); i ++){
if(map.containsKey(s.charAt(i))){
left = Math.max(left,map.get(s.charAt(i)) + 1);
}
map.put(s.charAt(i),i);
max = Math.max(max,i-left+1);
}
return max;
}
}
解法二:精简版滑动窗口
class Solution {
public int lengthOfLongestSubstring(String s) {
int[] m = new int[128];
int len = 0;
for(int i = 0, j = 0; j < s.length(); j++){
i = Math.max(m[s.charAt(j)], i);
len = Math.max(len, j - i + 1);
m[s.charAt(j)] = j + 1;
}
return len;
}
}
给定一个二进制数组 nums
和一个整数 k
,假设最多可以翻转 k
个 0
,则返回执行操作后 数组中连续 1
的最大个数 。
示例 1:
输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2 输出:6 解释:[1,1,1,0,0,1,1,1,1,1,1] 粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:
输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3 输出:10 解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1] 粗体数字从 0 翻转到 1,最长的子数组长度为 10。
解法:滑动窗口
统计窗口内 0 的个数 cnt0,则问题转换成在 cnt0≤k 的前提下,窗口大小的最大值。
class Solution {
public int longestOnes(int[] nums, int k) {
// 初始化最长子数组长度为0
int ans = 0;
// 初始化当前窗口中0的个数为0
int cnt0 = 0;
// 初始化左指针位置为0
int left = 0;
// 遍历数组,右指针从0到数组末尾
for (int right = 0; right < nums.length; right++) {
// 如果当前元素是0,则增加cnt0计数
cnt0 += 1 - nums[right]; // 0 变成 1,用来统计 cnt0
// 如果当前窗口中的0的个数超过k个,移动左指针直到窗口中的0的个数不超过k
while (cnt0 > k) {
// 减少左指针位置的0的计数
cnt0 -= 1 - nums[left++];
}
// 更新最长子数组的长度
ans = Math.max(ans, right - left + 1);
}
// 返回最长子数组的长度
return ans;
}
}
有一个书店老板,他的书店开了 n
分钟。每分钟都有一些顾客进入这家商店。给定一个长度为 n
的整数数组 customers
,其中 customers[i]
是在第 i
分钟开始时进入商店的顾客数量,所有这些顾客在第 i
分钟结束后离开。
在某些分钟内,书店老板会生气。 如果书店老板在第 i
分钟生气,那么 grumpy[i] = 1
,否则 grumpy[i] = 0
。
当书店老板生气时,那一分钟的顾客就会不满意,若老板不生气则顾客是满意的。
书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续 minutes
分钟不生气,但却只能使用一次。
请你返回 这一天营业下来,最多有多少客户能够感到满意 。
示例 1:
输入:customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], minutes = 3 输出:16 解释:书店老板在最后 3 分钟保持冷静。 感到满意的最大客户数量 = 1 + 1 + 1 + 1 + 7 + 5 = 16.
示例 2:
输入:customers = [1], grumpy = [0], minutes = 1 输出:1
解法:定长滑动窗口
本题可以拆分成两个问题:
老板不生气时的顾客数量之和 s0这些顾客可以感到满意。
长度为 minutes 的连续子数组中,老板生气时的顾客数量之和 s1的最大值 maxS1 。这些顾客可以感到满意。
最终答案为 s0+maxS1。
class Solution {
public int maxSatisfied(int[] customers, int[] grumpy, int minutes) {
// 初始化一个长度为2的数组s,用于存储满意和不满意的总人数
int[] s = new int[2];
// 初始化最大满意值maxS1为0
int maxS1 = 0;
// 遍历customers数组
for (int i = 0; i < customers.length; i++) {
// 根据grumpy数组的值,将当前顾客的满意度加到对应的s数组中
s[grumpy[i]] += customers[i];
// 如果窗口长度不足minutes,继续下一个循环
if (i < minutes - 1) { // 窗口长度不足 minutes
continue;
}
// 更新最大满意值maxS1
maxS1 = Math.max(maxS1, s[1]);
// 窗口最左边元素离开窗口,如果该元素是满意的,则从s[1]中减去其满意度
s[1] -= grumpy[i - minutes + 1] > 0 ? customers[i - minutes + 1] : 0;
}
// 返回总的满意人数,即初始满意人数加上最大可能增加的满意人数
return s[0] + maxS1;
}
}
你有一个炸弹需要拆除,时间紧迫!你的情报员会给你一个长度为 n
的 循环 数组 code
以及一个密钥 k
。
为了获得正确的密码,你需要替换掉每一个数字。所有数字会 同时 被替换。
k > 0
,将第 i
个数字用 接下来 k
个数字之和替换。k < 0
,将第 i
个数字用 之前 k
个数字之和替换。k == 0
,将第 i
个数字用 0
替换。由于 code
是循环的, code[n-1]
下一个元素是 code[0]
,且 code[0]
前一个元素是 code[n-1]
。
给你 循环 数组 code
和整数密钥 k
,请你返回解密后的结果来拆除炸弹!
示例 1:
输入:code = [5,7,1,4], k = 3 输出:[12,10,16,13] 解释:每个数字都被接下来 3 个数字之和替换。解密后的密码为 [7+1+4, 1+4+5, 4+5+7, 5+7+1]。注意到数组是循环连接的。
示例 2:
输入:code = [1,2,3,4], k = 0 输出:[0,0,0,0] 解释:当 k 为 0 时,所有数字都被 0 替换。
示例 3:
输入:code = [2,4,9,3], k = -2 输出:[12,5,6,13] 解释:解密后的密码为 [3+9, 2+3, 4+2, 9+4] 。注意到数组是循环连接的。如果 k 是负数,那么和为 之前 的数字。
解法:滑动窗口
注意无论 k>0 还是 k<0,窗口都在向右移动,只有初始位置不同。所以确定好第一个窗口的位置,就可以把 k>0 和 k<0 两种情况合并起来了:
k>0,第一个窗口的的下标范围为 [1,k+1)。
k<0,第一个窗口的的下标范围为 [n−∣k∣,n)。
无论 k 是正是负,窗口的大小都是 ∣k∣。
在窗口向右滑动时,设移入窗口的元素下标为 rmodn,则移出窗口的元素下标为 (r−∣k∣)modn。
代码实现时,k=0 的特判可以省略。
class Solution {
public int[] decrypt(int[] code, int k) {
int n = code.length;
int[] ans = new int[n];
int r = k > 0 ? k + 1 : n; // 第一个窗口的右开端点
k = Math.abs(k);
int s = 0;
for (int i = r - k; i < r; i++) {
s += code[i]; // 计算 ans[0]
}
for (int i = 0; i < n; i++) {
ans[i] = s;
s += code[r % n] - code[(r - k) % n];
r++;
}
return ans;
}
}