1004.最大连续1的个数
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int ans=0,head=0;
int cnt[2]{};
for(int i=0;i<nums.size();i++){
cnt[nums[i]]++;
while(cnt[0]>k){
cnt[nums[head]]--;
head++;
}
ans=max(ans,i-head+1);
}
return ans;
}
};
1658.将 x 减到0的最小操作数 [逆向思维]
accumulate //计算容器元素累计总和
vector<int>V;
int sum = accumulate(V.begin(), V.end(), 0);//返回数组元素的总和
int sum1 = accumulate(V.begin(), V.end(), 100);//返回元素的总和+100=5150
把问题转换成「从 nums 中移除一个最长的子数组,使得剩余元素的和为 x」。
换句话说,要从 nums 中找最长的子数组,其元素和等于 s−x,这里 s 为 nums 所有元素之和。
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int target=accumulate(nums.begin(),nums.end(),0)-x; //子数组的元素和
if(target<0) return -1; //全部移除也无法满足要求
int ans=-1,head=0,sum=0,n=nums.size();
for(int i=0;i<n;i++){
sum+=nums[i];
while(sum>target){
sum-=nums[head];
head++;
}
if(sum==target) ans=max(ans,i-head+1); //存在,最长子数组的长度
}
if(ans==-1) return -1; //不存在
else return n-ans;
}
};
1838.最高频元素的频数 [类似2779.数组的最大美丽值]
将数组排序,遍历排序后数组每个元素作为目标值,并求出此时按贪心策略可以改变至目标值的元素左边界。
1.由于目标值一定是数组中已有的一个元素,频数最小为1
2.操作后的数必定 >= 原数,排序后目标值下标不可能为0,遍历从 i=1 开始
class Solution {
public:
int maxFrequency(vector<int>& nums, int k) {
ranges::sort(nums); //1 <= k <= 10^5
int n=nums.size(),ans=1,head=0;
long long total=0;
for(int i=1;i<n;i++){
total+=(long long)(nums[i]-nums[i-1])*(i-head);
//前驱前进后需要增加的操作次数,可能超过k,用long long维护
while(total>k){
total-=nums[i]-nums[head];
head++;
}
ans=max(ans,i-head+1);
}
return ans;
}
};
2516.每种字符至少取k个
在两头取拿的问题一般都可以转化为中间的连续滑窗
class Solution {
public:
int takeCharacters(string s, int k) {
int ans=0,head=0,n=s.length();
int cnt[100]{};
for(int x=0;x<n;x++) cnt[s[x]]++;
if(cnt[97]<k|| cnt[98]<k|| cnt[99]<k) return -1;
for(int i=0;i<n;i++){
cnt[s[i]]--;
while(cnt[97]<k|| cnt[98]<k|| cnt[99]<k){
cnt[s[head]]++;
head++;
}
ans=max(ans,i-head+1);
}
return n-ans;
}
};
2831.找出最长等值子数组
把相同元素分组,相同元素的下标记录到哈希表(或者数组)posLists 中。
例如,元素 3 在 nums 中的下标有 1,3,5,那么 posLists[3]=[1,3,5]。
emplace_back()在实现时,直接在容器的尾部创建这个元素,省去了拷贝或移动元素的过程。
pos[nums[i]].emplace_back(i); //在数组尾部插入
auto
:自动类型推导关键字,编译器会根据pos
中的元素类型自动推导出循环变量的类型。&
:表示按引用捕获,这意味着循环变量将直接引用pos
中的元素,而不是它们的副本。
for (auto &[_, vec] : pos)
上述代码的意思是:对于容器pos
中的每个元素,将其解构为两个部分,忽略第一个部分,并将第二个部分的引用赋值给变量vec
,然后执行循环体中的代码
遍历 posLists 中的每个下标列表 pos,如果上式大于 k,说明删的数太多了,那么移动左指针 left,直到上式小于等于 k,此时用 right−left+1 更新答案的最大值。
class Solution {
public:
int longestEqualSubarray(vector<int>& nums, int k) {
int n=nums.size();
unordered_map<int,vector<int>>pos;
for(int i=0;i<n;i++){
pos[nums[i]].emplace_back(i);
}
int ans=0;
for(auto &[_,vec]:pos){ //遍历pos中每个下标列表
for(int i=0,j=0;i<vec.size();i++){ //i,j为滑动窗口左右端点
//vec[i]-vec[j]+1 相同元素在原数组中的实际距离(删除前子数组长度)
//i-j+1 该元素重复出现次数,两者之差为要删除的元素个数
while((vec[i]-vec[j]+1)-(i-j+1)>k) j++;
ans=max(ans,i-j+1);
}
}
return ans;
}
};