定长滑动窗口-基础篇(2)

这一篇文章是属于上一篇文章的后续,主要是以写题目为主,如果是要学习定长滑动窗口的的基础概念,可以看我的上一篇文章。

1.题目得到 K 个黑块的最少涂色次数

给你一个长度为 n 下标从 0 开始的字符串 blocksblocks[i] 要么是 'W' 要么是 'B' ,表示第 i 块的颜色。字符 'W''B' 分别表示白色和黑色。

给你一个整数 k ,表示想要 连续 黑色块的数目。

每一次操作中,你可以选择一个白色块将它 涂成 黑色块。

请你返回至少出现 一次 连续 k 个黑色块的 最少 操作次数。

cpp 复制代码
class Solution {
public:
    int minimumRecolors(string blocks, int k) {
        int cnt_w=0;
        for(int i=0;i<k;i++){
            if(blocks[i]=='W') cnt_w++;
        }
        int ans=cnt_w;
        for(int i=k;i<blocks.size();i++){
            cnt_w+=(blocks[i]&1)-(blocks[i-k]&1);
            ans=min(ans,cnt_w);
        }
        return ans;
    }
};

本题可以看作是一个长度为k的滑动窗口,我们需要计算这个窗口内的W的出现次数的最小值。

窗口初始位于 blocks 的长为 k 的前缀上,那么初始化 cntW 为这个前缀的 'W' 的个数。然后不断向右滑动窗口,如果窗口内少了 'W',则 cntW 减一;如果窗口内多了 'W',则 cntW 加一。

取滑动中的 cntW 的最小值,即为答案。

block【i】&1是什么意思?

对于本题,可以把 'W' 看成 1,把 'B' 看成 0,这恰好是它们 ASCII 码二进制最低位的值。

2.题目几乎唯一子数组的最大和

给你一个整数数组 nums 和两个正整数 mk

请你返回 nums 中长度为 k几乎唯一 子数组的 最大和 ,如果不存在几乎唯一子数组,请你返回 0

如果 nums 的一个子数组有至少 m 个互不相同的元素,我们称它是 几乎唯一 子数组。

子数组指的是一个数组中一段连续 非空 的元素序列。

cpp 复制代码
class Solution {
public:
    long long maxSum(vector<int>& nums, int m, int k) {
        long long sum=0,ans=0;
        unordered_map<int,int> cnt;
        for(int i=0;i<nums.size();i++){
            sum+=nums[i];
            cnt[nums[i]]++;
            int left=i-k+1;
            if(left<0) continue;
            if(cnt.size()>=m){
                ans=max(ans,sum);
            }
            sum-=nums[left];
            if(--cnt[nums[left]]==0) cnt.erase(nums[left]);
        }
        return ans;
    }
};

这道题要注意的点是这个函数的返回值是long long,还有就是要使用哈希表对数组的数字进行存储。

入:元素 x=nums[i] 进入窗口,把 x 加到元素和 s 中,把 x 加到哈希表中(统计 x 的出现次数)。如果 i<k−1 则重复第一步。

更新:如果哈希表的大小 ≥m,用 s 更新答案的最大值。

出:元素 x=nums[i−k+1] 离开窗口,把 s 减少 x,把哈希表中 x 的出现次数减一。如果 x 的出现次数变成 0,要从哈希表中删除 x,否则哈希表的大小不正确。

也就是说要把key和value都要进行删除。

3.题目长度为 K 子数组中的最大和

给你一个整数数组 nums 和一个整数 k 。请你从 nums 中满足下述条件的全部子数组中找出最大子数组和:

  • 子数组的长度是 k,且
  • 子数组中的所有元素 各不相同 。

返回满足题面要求的最大子数组和。如果不存在子数组满足这些条件,返回 0

子数组 是数组中一段连续非空的元素序列。

cpp 复制代码
class Solution {
public:
    long long maximumSubarraySum(vector<int>& nums, int k) {
        long long ans=0,sum=0;
        unordered_map<int,int> cnt;
        for(int i=0;i<nums.size();i++){
            sum+=nums[i];
            cnt[nums[i]]++;
            int left=i-k+1;
            if(left<0) continue;
            if(cnt.size()==k) ans=max(ans,sum);
            sum-=nums[left];
            if(--cnt[nums[left]]==0) cnt.erase(nums[left]);
        }
        return ans;
    }
};

这道题目跟上一道题目非常相似,就是把>=m换成==k,因为这道题目可以看成上一道题目中的长度为k的子数组里面至少有k个互不相同的元素,也就是说这道题目的子数组是不能有相同的元素。

再加上unordered_map是有去重的功能的,当这个哈希表中的长度为k的时候,才会进行更新ans操作,这样才能保证结果的正确。

4.题目可获得的最大点数

几张卡牌排成一行 ,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。

每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。

你的点数就是你拿到手中的所有卡牌的点数之和。

给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。

cpp 复制代码
class Solution {
public:
    int maxScore(vector<int>& cardPoints, int k) {
        int n=cardPoints.size();
        int ans=0;
        int sum=0;
        int m=n-k;//滑动窗口的长度
        for(int i=0;i<m;i++){
            sum+=cardPoints[i];
        }
        int min_sum=sum;
        for(int i=m;i<n;i++){
            sum+=cardPoints[i];
            sum-=cardPoints[i-m];
            min_sum=min(min_sum,sum);
        }
        for(int i=0;i<n;i++){
            ans+=cardPoints[i];
        }
        return ans-min_sum;
    }
};

这里我们可以用一个逆向思维来解决这个问题。

拿走 k 张,剩下 n−k 张。这里 n 是 cardPoints 的长度。

由于拿走的点数和 + 剩下的点数和 = 所有点数和 = 常数,所以为了最大化拿走的点数和,应当最小化剩下的点数和。

由于只能从开头或末尾拿牌,所以最后剩下的牌必然是连续的。

至此,问题变成:

计算长为 n−k 的连续子数组和的最小值。

这可以用定长滑动窗口解决。

我们在写这个代码的时候,知道了长度之后,也就是m之后,就不用管k了,直接看m就可以了。

我们先对前m个元素进行求和,对min_sum进行初始化。

然后我们的数组下标直到m,进行循环求出后面多个子数组的和,也就是我之前说的定长滑动窗口的三部曲,入------更新------出。

这样我们就求出了min_sum的最小值,也就是剩下的点数和的最小值,我们最后再求出cardPoints的总和,再减去min_sum,得到的结果就是这道题目的答案。

相关推荐
小王努力学编程2 小时前
LangChain——AI应用开发框架(核心组件1)
linux·服务器·前端·数据库·c++·人工智能·langchain
庄小焱2 小时前
【机器学习】——房屋销售价格预测实战
人工智能·算法·机器学习·预测模型
txzrxz2 小时前
单调栈详解(含题目)
数据结构·c++·算法·前缀和·单调栈
AI科技星2 小时前
张祥前统一场论的数学表述与概念梳理:从几何公设到统一场方程
人工智能·线性代数·算法·机器学习·矩阵·数据挖掘
程序员-King.2 小时前
day167—递归—二叉树的直径(LeetCode-543)
算法·leetcode·深度优先·递归
亲爱的非洲野猪2 小时前
2动态规划进阶:背包问题详解与实战
算法·动态规划·代理模式
Trouvaille ~2 小时前
【Linux】进程间通信(二):命名管道与进程池架构实战
linux·c++·chrome·架构·进程间通信·命名管道·进程池
YH12312359h2 小时前
战斗机目标检测与跟踪:YOLOv26算法详解与应用
算法·yolo·目标检测
芒克芒克3 小时前
LeetCode 134. 加油站(O(n)时间+O(1)空间最优解)
java·算法·leetcode·职场和发展