七月集训(8)前缀和

1.LeetCode:2256. 最小平均差

原题链接


        给你一个下标从 0 开始长度为 n 的整数数组 nums 。

        下标 i 处的 平均差 指的是 nums 中 前 i + 1 个元素平均值和 后 n - i - 1 个元素平均值的 绝对差 。两个平均值都需要 向下取整 到最近的整数。

        请你返回产生 最小平均差 的下标。如果有多个下标最小平均差相等,请你返回 最小 的一个下标。

        注意:

        两个数的 绝对差 是两者差的绝对值。

         n 个元素的平均值是 n 个元素之 和 除以(整数除法) n 。

        0 个元素的平均值视为 0 。

        示例 1:

        输入:nums = [2,5,3,9,5,3]

        输出:3

        示例 2:

        输入:nums = [0]

        输出:0

        提示:

        1 <= nums.length <= 1e5

        0 <= nums[i] <= 1e5


        首先计算出数组元素和,然后从前往后遍历即可。

class Solution {
public:
    int minimumAverageDifference(vector<int>& nums) {
        long long prev=0;
        int ret=0x3f3f3f3f,ans;
        int n=nums.size();
        long long sum=0;
        for(auto i:nums){
            sum+=i;
        }
        for(int i=0;i<n;++i){
            prev+=nums[i];
            int tmp;
            if(i!=n-1)tmp=abs(prev/(i+1)-(sum-prev)/(n-i-1));
            else tmp=abs(prev/(i+1));
            if(tmp<ret){
                ret=tmp;
                ans=i;
            }   
        }
        return ans;
    }
};

2.LeetCode:1737. 满足三条件之一需改变的最少字符数

原题链接


        给你两个字符串 a 和 b ,二者均由小写字母组成。一步操作中,你可以将 a 或 b 中的 任一字符 改变为 任一小写字母 。

        操作的最终目标是满足下列三个条件 之一 :

        a 中的 每个字母 在字母表中 严格小于 b 中的 每个字母 。

        b 中的 每个字母 在字母表中 严格小于 a 中的 每个字母 。

        a 和 b 都 由 同一个 字母组成。

        返回达成目标所需的 最少 操作数。

        示例 1:

        输入:a = “aba”, b = “caa”

        输出:2

        示例 2:

        输入:a = “dabadd”, b = “cda”

        输出:3

        提示:

        1 <= a.length, b.length <= 1e5

        a 和 b 只由小写字母组成


        这道题容易想到的就是三个条件都走一遍,不过其实不用这么麻烦,我们考虑前两个条件,其实就是找到一个合适的字母,使得让一个字符串都小于他另一个字符串都大于等于他的改变次数最小(比如aaa ,abc ,这个合适的字母就是b,使abc变为bbc即可)。而第三个条件只需要找到两个字符串出现公共字母最多那个并修改即可。


        前两个为什么是这样,难道不能遍历来求吗?这个肯定是可以的,只需要统计两个字符串的词频,然后分情况讨论即可。不过这种做法下要讨论的情况实在是太多了,比如如果两个字符串最小字母均为a怎么办?一个字符串出现频率最大的字母大于另一个字符串最小的字母怎么办?如果两个字符串最小字母为z怎么办?这些都是我们要考虑的问题。那么既然这样为什么不能退而求其次找一个中间值,让一个字符串均小于他,另一个字符串均大于他,并且这样做我们遍历枚举的那种做法也一定会处理到(让某个字符串均小于他而另一个字符串均大于他。那么这种情况下修改的次数就是 nstr1-prea+preb,prea和preb都是a,b字符串中小于我们选择的标准的个数)

        具体做法就是先统计两个字符串每个字母出现的频率,我们遍历a-y(没有严格小于z的字母,所以如果选z为标准只能以条件三来判断),找出两个字符串该字母出现的频率,然后选择(nstr1-prea+preb,nstr2-preb+prea)的最小值并维护ans,同时第三个条件也可以在途中判断。最后再单独判断下z即可。

class Solution {
public:
    int minCharacters(string a, string b) {
        vector<int> cnt1(26),cnt2(26);
        for(auto i:a) cnt1[i-'a']++;
        for(auto i:b) cnt2[i-'a']++;
        int na=a.size(),nb=b.size();
        int prea=0,preb=0,ans=INT_MAX;
        for(int i=0;i<25;++i){
            prea+=cnt1[i];
            preb+=cnt2[i];
            ans=min(ans,min(na-prea+preb,nb-preb+prea));
            ans=min(ans,na-cnt1[i]+nb-cnt2[i]);
        }
        ans=min(ans,na-cnt1[25]+nb-cnt2[25]);
        return ans;
    }
};

3.LeetCode:1894. 找到需要补充粉笔的学生编号

原题链接


        一个班级里有 n 个学生,编号为 0 到 n - 1 。每个学生会依次回答问题,编号为 0 的学生先回答,然后是编号为 1 的学生,以此类推,直到编号为 n - 1 的学生,然后老师会重复这个过程,重新从编号为 0 的学生开始回答问题。

        给你一个长度为 n 且下标从 0 开始的整数数组 chalk 和一个整数 k 。一开始粉笔盒里总共有 k 支粉笔。当编号为 i 的学生回答问题时,他会消耗 chalk[i] 支粉笔。如果剩余粉笔数量 严格小于 chalk[i] ,那么学生 i 需要 补充 粉笔。

        请你返回需要 补充 粉笔的学生 编号 。

        示例 :

        输入:chalk = [3,4,1,2], k = 25

        输出:1

        提示:

        chalk.length == n

        1 <= n <= 1e5

        1 <= chalk[i] <= 1e5

        1 <= k <= 1e9


        本题就没什么好说的了,先求出chalk的总和然后让k模上他,然后遍历数组找到第一个让k小于当前所拿粉笔的下标即可。不过注意求和的时候要用longlong,1e5*1e5会爆int
(为啥不是sum<=k,因为等于的时候当前学生还有粉笔用,下一个为0需要补粉笔)

class Solution {
public:
    int chalkReplacer(vector<int>& chalk, int k) {
        long long sum=0;
        for(auto i:chalk){
            sum+=i;
        }
        k%=sum;
        sum=0;
        for(int i=0;i<chalk.size();++i){
            sum+=chalk[i];
            if(k<sum){
                return i;
            }
        }
        return -1;
    }
};

4.LeetCode:2055. 蜡烛之间的盘子

原题链接


        给你一个长桌子,桌子上盘子和蜡烛排成一列。给你一个下标从 0 开始的字符串 s ,它只包含字符 ‘’ 和 ‘|’ ,其中 '’ 表示一个 盘子 ,‘|’ 表示一支 蜡烛 。

        同时给你一个下标从 0 开始的二维整数数组 queries ,其中 queries[i] = [lefti, righti] 表示 子字符串 s[lefti…righti] (包含左右端点的字符)。对于每个查询,你需要找到 子字符串中 在 两支蜡烛之间 的盘子的 数目 。如果一个盘子在 子字符串中 左边和右边 都 至少有一支蜡烛,那么这个盘子满足在 两支蜡烛之间 。

        比方说,s = “|| * * | | * * | " ,查询 [3, 8] ,表示的是子字符串 " * || * * |" 。子字符串中在两支蜡烛之间的盘子数目为 2 ,子字符串中右边两个盘子在它们左边和右边 都 至少有一支蜡烛。

        请你返回一个整数数组 answer ,其中 answer[i] 是第 i 个查询的答案。

        示例 1:

        输入:s = "
* | * * | * * * |”, queries = [[2,5],[5,9]]

        输出:[2,3]

        示例 2:

        输入:s = " * * * | * * | * * * * * | * * | | * * | * ", queries = [[1,17],[4,5],[14,17],[5,11],[15,16]]

        输出:[9,0,0,0,0]

        提示:

        3 <= s.length <= 1e5

        s 只包含字符 ‘*’ 和 ‘|’ 。

        1 <= queries.length <= 1e5

        queries[i].length == 2

        0 <= lefti <= righti < s.length


        本题就是考查前缀和的基础应用了,我们利用前缀和先统计每个位置的’*'的个数,然后再利用两个数组统计每个位置左边和右边第一个蜡烛的位置。不过这里需要对一些非法状况进行处理,左边没有蜡烛就设置为-1,右边没有蜡烛就设置为n+1,还有该位置是蜡烛的话那么两个数组中对应位置都是自己(前缀和数组下标通常从1开始这里是为了对应)

        然后就简单了,对于每次查询,先找到左边界的第一个右蜡烛,右边界的第一个左蜡烛,再利用前缀和数组得到这个区间内的盘子数量即可。

class Solution {
public:
    vector<int> ans;
    vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
       int n=s.length();
       vector<int> left(n),right(n);
       vector<int> sum(n+1);
       int pos=-1;
       for(int i=0;i<n;i++)
       {
           if(s[i]=='*')
           left[i]=pos;
           else pos=i,left[i]=pos;
       }
       pos=n+1;
       for(int i=n-1;i>=0;i--)
       {
           if(s[i]=='*')
           right[i]=pos;
           else pos=i,right[i]=pos;
       }
       for(int i=1;i<=n;i++) {
           sum[i]=s[i-1]=='*'?sum[i-1]+1:sum[i-1];
       }
       for(auto i:queries)
       {
           int a=i[0],b=i[1];
           int ll=right[a],rr=left[b];
           if(ll<rr&&ll!=-1&&rr!=n+1)
           ans.push_back(sum[rr+1]-sum[ll+1]);
           //rr,ll位置都是蜡烛,所以只需要对应到前缀和数组下标即可
           else ans.push_back(0);
       }
       return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值