滑动窗口 leetcode.209.3.1004.1208

209. 长度最小的子数组

给定一个含有 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;
    }
}

3. 无重复字符的最长子串

给定一个字符串 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;
    }
}

1004. 最大连续1的个数 III

给定一个二进制数组 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;
    }
}

1052. 爱生气的书店老板

有一个书店老板,他的书店开了 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;
    }
}

1652. 拆炸弹

你有一个炸弹需要拆除,时间紧迫!你的情报员会给你一个长度为 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;
    }
}

 

 

 

你可能感兴趣的:(算法,java,数据结构,滑动窗口)