前缀数组后缀数组

本文介绍了两道算法题目,分别是使字符串平衡的最少删除次数和将x减到0的最小操作数。在字符串问题中,目标是删除'a'和'b'组成的字符串中的某些字符,使其变得平衡;在数组问题中,探讨了如何通过移除数组元素来恰好将一个整数x减少到0。文章提到了滑动窗口作为一种可能的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

连续两天的周赛,双周赛都用到了,记录一下。

1653. 使字符串平衡的最少删除次数

给你一个字符串 s ,它仅包含字符 ‘a’ 和 'b’​​​​ 。

你可以删除 s 中任意数目的字符,使得 s 平衡 。我们称 s 平衡的 当不存在下标对 (i,j) 满足 i < j 且 s[i] = ‘b’ 同时 s[j]= ‘a’ 。

请你返回使 s 平衡 的 最少 删除次数。

输入:s = "aababbab"
输出:2
解释:你可以选择以下任意一种方案:
下标从 0 开始,删除第 2 和第 6 个字符("aababbab" -> "aaabbb"),
下标从 0 开始,删除第 3 和第 6 个字符("aababbab" -> "aabbbb")。


class Solution {
public:
    int minimumDeletions(string s) {
        int len = s.size();
        vector<int>pre(len,0), suf(len,0);
        for(int i = 0; i < len; i++){
            if(i > 0) pre[i] = pre[i-1];
            pre[i] += s[i] =='b';
        }
        for(int i = len-1; i>=0; i--){
            if(i < len-1) suf[i] = suf[i+1];
            suf[i] += s[i] =='a';
        }
        int ans = min(pre[len-1], suf[0]);
        for(int i = 1; i < len; i++){
            ans = min(ans, pre[i-1]+suf[i]);
        }
        return ans;
    }
};

1658. 将 x 减到 0 的最小操作数

给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。

如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1 。

示例 1:

输入:nums = [1,1,4,2,3], x = 5
输出:2
解释:最佳解决方案是移除后两个元素,将 x 减到 0 。
class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int len = nums.size();
        vector<long long>pre(len), suf(len);
        int ant = INT_MAX;
        
        for(int i = 0; i < len; i++){
            if(i) pre[i] = pre[i-1];
            pre[i]+=nums[i];
            if(pre[i] == x) ant = min(ant, i+1);   
        }
       
        for(int i = len-1; i>=0; i--){
            if(i < len-1) suf[i] = suf[i+1];
            suf[i] += nums[i];
            if(suf[i] == x) ant = min(ant, len-i);
        }
        
       //  第一种写法,超时,两层循环, pre递增,suf递增,时间负载度o(n^2)
        // for(int i = 0; i < len; i++){
        //     if(pre[i] > x) break;
        //     else{
        //         int j = len-1;
        //         while(j > i && pre[i] + suf[j] < x)j--;
        //         if(j > i && pre[i] + suf[j] == x)ant = min(ant, i + 1 + len-j);
        //     }
        // }
        
        // 第二种写法,ac 改变suf遍历顺序,递减遍历,因为pre也是递增遍历,j永远不回头,时间复杂度o(n)
        for(int i = 0, j = 1; i + 1 < len; i += 1){
             j = max(j, i + 1);
           
            while(j < len and pre[i] + suf[j] > x) j += 1;
            if(j < len and pre[i] + suf[j] == x) ant = min(ant, i + 1 + len - j);
        }
        
       
        return ant == INT_MAX?-1:ant;
    }
    
};

—注:本题也可以使用滑动窗口,找到sum-x的连续窗口。

补充,滑动窗口实现

class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int target = -x;
        for(const int &a:nums){
            target+=a;
        }
        if(target < 0) return -1;
        int left = 0, right = 0, sum = 0,ans = INT_MIN;
        int len = nums.size();
        while(left < len){
            if(right < len){
                sum += nums[right++];
            }
            while(sum >= target){
                // 记录最大的窗口尺寸
                if(sum == target) ans = max(ans, right-left);
                sum-=nums[left++];
            }
            // 这里可以直接break了,下面写法也ac
            if(right == len)left++;
            // if(right == len) break;

        }
        int res = ans==INT_MIN?-1: len-ans;
        return res;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值