LeetCode---374周赛

2023-12-13 20:29:29

题目列表

2951. 找出峰值

2952. 需要添加的硬币的最小数量

2953. 统计完全子字符串

2954. 统计感冒序列的数目

一、找到峰值

这个简单的模拟,代码如下

class Solution {
public:
    vector<int> findPeaks(vector<int>& mountain) {
        int n=mountain.size();
        vector<int>ans;
        for(int i=1;i<n-1;i++){
            if(mountain[i]>mountain[i-1]&&mountain[i]>mountain[i+1])
                ans.push_back(i);
        }
        return ans;
    }
};

二、需要添加的硬币的最小数量

题目要求我们添加最小的金币数,使得我们能用已有的金币凑出1~target中的任何一个数值,这题看着简单,但是思维难度比第三题高,是那种思维题,这里讲一下思路:要先排序,因为我们在顺序时,会更容易找出规律

我们要保证1~target中的每一个元素都能被构造,那么什么情况下一个数是不能被构造出来的?假设我们能用前i个数构造出[1,x],现在我们要构造x+1这个数,怎么办?

1.如果coins[i+1] == x+1,我们当然能构造出来x+1,且一直到x+x+1我们都能构造出来

2.如果coins[i+1] < x+1,我们也能构造出x+1,且一直到x+coin[i+1]我们都能构造出来

3.如果coins[i+1]>x+1呢?显然我们就无法构造出x+1了(因为我们已经排过序了,所以coins[i+1]!=1),那么我们是补1呢?还是直接补一个x+1?显然我们肯定是补x+1,这样我们能构造出的数据区间最大为[1,2*x+1]

如此循环,得到答案,因为我们每次加的金币都会让数据区间最大化,贪心的思路如下图

这题的关键是我们要往加粗的两个语句上去思考,本质是一个数学归纳的思想

代码如下

class Solution {
public:
    int minimumAddedCoins(vector<int>& coins, int target) {
        int ans=0;
        int t=0;//表示[1,t]区间内的值可以构造
        sort(coins.begin(),coins.end());
        int i=0,n=coins.size();//用来遍历数组
        while(t<target){
            while(i<n&&t+1>=coins[i]){
                t+=coins[i];
                i++;
                if(t>=target)
                    return ans;
            }
            t+=(t+1);
            ans++;
        }
        return ans;
    }
};

?三、统计完全子字符串

这题的完全字符串有两个条件,分别对应我们思路的两个部分:

1.条件1是标准的滑动窗口问题,共有26种固定大小的窗口需要考虑

2.条件2意味着需要将字符串拆分成符合条件的子字符串来考虑,因为子字符串是连续的,一旦出现两个相邻字符的大小相差>2,那么就不可能有一个满足条件的子字符串同时包含这两个字符

代码如下

class Solution {
public:
    int countCompleteSubstrings(string word, int k) {
        int i=0;
        int n=word.size(),ans=0;
        function<int(int,int)>getNum=[&](int start,int end)->int{
            int ret=0;
            for(int i=1;i<=26;i++){//一共有26个大小不同的窗口
                //滑动窗口
                int sz=i*k;
                if(sz>end-start) break;
                int cnt[26]={0};
                for(int l=start,r=start,s=0;r<end;r++){
                    int idx=word[r]-'a';
                    if(++cnt[idx]==k) s++;
                    if(cnt[idx]==k+1) s--;//注意不是>k
                    if(r-l+1>sz){
                        idx=word[l]-'a';
                        if(--cnt[idx]==k) s++;
                        if(cnt[idx]==k-1) s--;//注意不是<k
                        l++;
                    }
                    if(i==s) ret++;
                }  
            }
            return ret;
        };

        while(i<n){
            int begin=i;
            i++;
            while(i<n&&abs(word[i]-word[i-1])<=2)
                i++;
            if(i-begin>=k) ans+=getNum(begin,i);//[begin,i)的区间符合条件二 
        }
        return ans;
    }
};

四、统计感冒序列的数目

数学题,题解如下?

?代码如下

const int MX=100000;
const int MOD=1e9+7;
typedef long long LL;
LL fac[MX],inv_fac[MX];
//快速幂
LL POW(LL x,LL y){
    LL ret=1;
    while(y){
        if(y&1) ret=(ret*x)%MOD;
        x=(x*x)%MOD;
        y>>=1;
    }
    return ret;
}

//预处理
int init=[](){
    fac[0]=1;
    for(int i=1;i<MX;i++){
        fac[i]=fac[i-1]*i%MOD;
    }

    //逆元
    inv_fac[MX-1]=POW(fac[MX-1],MOD-2);
    for(int i=MX-1;i>0;i--){
        inv_fac[i-1]=inv_fac[i]*i%MOD;
    }
    return 0;
}();

long long comb(int n,int k){//n!/((n-k)!*k!)
    return fac[n]*inv_fac[k]%MOD*inv_fac[n-k]%MOD;
}

class Solution {
public:
    int numberOfSequence(int n, vector<int>& sick) {
        int m=sick.size();
        int total=n-m;
        long long ans=comb(total,sick[0])*comb(total-sick[0],n-sick.back()-1)%MOD;
        total-=sick[0]+n-sick.back()-1;
        int x=0;
        for(int i=0;i<m-1;i++){
            int k=sick[i+1]-sick[i]-1;
            if(k){
                x+=k-1;
                ans=ans*comb(total,k)%MOD;
                total-=k;
            }
        }
        return ans*POW(2,x)%MOD;
    }
};

文章来源:https://blog.csdn.net/V_zjs/article/details/134877413
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。