1.题目删除子数组的最大得分
给你一个正整数数组 nums ,请你从中删除一个含有 若干不同元素 的子数组**。** 删除子数组的 得分 就是子数组各元素之 和 。
返回 只删除一个 子数组可获得的 最大得分 。
如果数组 b 是数组 a 的一个连续子序列,即如果它等于 a[l],a[l+1],...,a[r] ,那么它就是 a 的一个子数组。
输入:nums = [4,2,4,5,6]
输出:17
解释:最优子数组是 [2,4,5,6]
这道题翻译成人话就是:找没有重复元素的子数组,统计最大和,子数组包含隐藏条件------连续,不是子序列。
cpp
class Solution {
public:
int maximumUniqueSubarray(vector<int>& nums) {
unordered_set<int> st;
int ans=0;
int sum=0;
int left=0;
for(int x : nums){
while(st.contains(x)){
st.erase(nums[left]);
sum-=nums[left];
left++;
}
st.insert(x);
sum+=x;
ans=max(ans,sum);
}
return ans;
}
};
这道题是跟力扣的第三题.无重复字符的最长子串是一样的,额外维护窗口中的元素和即可。
2.题目最多 K 个重复元素的最长子数组
给你一个整数数组 nums 和一个整数 k 。
一个元素 x 在数组中的 频率 指的是它在数组中的出现次数。
如果一个数组中所有元素的频率都 小于等于 k ,那么我们称这个数组是 好 数组。
请你返回 nums 中 最长好 子数组的长度。
子数组 指的是一个数组中一段连续非空的元素序列。
输入:nums = [1,2,3,1,2,3,1,2], k = 2
输出:6
解释:最长好子数组是 [1,2,3,1,2,3] ,值 1 ,2 和 3 在子数组中的频率都没有超过 k = 2 。[2,3,1,2,3,1] 和 [3,1,2,3,1,2] 也是好子数组。
最长好子数组的长度为 6 。
cpp
class Solution {
public:
int maxSubarrayLength(vector<int>& nums, int k) {
int ans=0;
int left=0;
unordered_map<int,int> cnt;
for(int right=0;right<nums.size();right++){
cnt[nums[right]]++;
while(cnt[nums[right]]>k){
cnt[nums[left]]--;
left++;
}
ans=max(ans,right-left+1);
}
return ans;
}
};
对于本题,新加入元素 x=nums[right] 后,如果 x 的出现次数超过 k,则不断右移左指针 left,直到窗口内的 x 的出现次数等于 k 为止,然后用窗口大小 right−left+1 更新答案的最大值。
3.题目考试的最大困扰度
一位老师正在出一场由 n 道判断题构成的考试,每道题的答案为 true (用 'T' 表示)或者 false (用 'F' 表示)。老师想增加学生对自己做出答案的不确定性,方法是 最大化 有 连续相同 结果的题数。(也就是连续出现 true 或者连续出现 false)。
给你一个字符串 answerKey ,其中 answerKey[i] 是第 i 个问题的正确结果。除此以外,还给你一个整数 k ,表示你能进行以下操作的最多次数:
- 每次操作中,将问题的正确答案改为
'T'或者'F'(也就是将answerKey[i]改为'T'或者'F')。
请你返回在不超过 k 次操作的情况下,最大 连续 'T' 或者 'F' 的数目。
输入:answerKey = "TTFF", k = 2
输出:4
解释:我们可以将两个 'F' 都变为 'T' ,得到 answerKey = "TTTT" 。
总共有四个连续的 'T' 。
cpp
class Solution {
public:
int maxConsecutiveAnswers(string answerKey, int k) {
int t=0;
int f=0;
int left=0;
int ans=0;
for(int right=0;right<answerKey.size();right++){
if(answerKey[right]=='T') t++;
else f++;
while(min(t,f)>k){
if(answerKey[left]=='T') t--;
else f--;
left++;
}
ans=max(ans,right-left+1);
}
return ans;
}
};
这道题目的意思就是:求 answerKey 的一个最长子串,包含至多 k 个 T 或者至多 k 个 F。
由于子串越长,越无法满足要求,有单调性,可以用滑动窗口解决。
1.遍历 answerKey,枚举子串右端点 right,同时维护最小左端点 left 以及子串中的字符个数 cnt。
2.把 answerKey[right] 的出现次数加一。
3.如果 T 和 F 的出现次数都超过 k,那么必须不断移动左端点 left,同时减少 answerKey[left] 的出现次数,直到 T 和 F 的出现次数至少有一个 ≤k。
4.循环结束后,说明子串右端点在 right 时,对应的最小左端点为 left,用子串长度 right−left+1 更新答案的最大值。
5.遍历 answerKey 结束后,返回答案。
我这个题解写的稍微清楚一点,其实也可以使用位运算来写,这样可以更快一点。
4.题目最大连续1的个数 III
给定一个二进制数组 nums 和一个整数 k,假设最多可以翻转 k 个 0 ,则返回执行操作后 数组中连续 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。
cpp
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int ans=0;
int left=0;
int cnt=0;
for(int right=0;right<nums.size();right++){
if(nums[right]==0) cnt++;
while(cnt>k){
if(nums[left]==0) cnt--;
left++;
}
ans=max(ans,right-left+1);
}
return ans;
}
};
cnt0 += 1 - nums[right]和cnt0 -= 1 - nums[left]
就相当于
if (nums[right] == 0) cnt0++;
if (nums[left] == 0) cnt0--;
这里是我之前写的代码,但是不方便理解,题解这样写就可以更好理解一些。
统计窗口内 0 的个数 cnt0,则问题转化成在 cnt0≤k 的前提下,窗口大小的最大值。