这一篇文章是属于上一篇文章的后续,主要是以写题目为主,如果是要学习定长滑动窗口的的基础概念,可以看我的上一篇文章。
1.题目得到 K 个黑块的最少涂色次数
给你一个长度为 n 下标从 0 开始的字符串 blocks ,blocks[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 和两个正整数 m 和 k 。
请你返回 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,得到的结果就是这道题目的答案。