10.15总结

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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值