• 牛客多校第五场 G subsequence 1 最长公共子序列/组合数


    题意:

    给定两个由数字组成的序列s,t,找出s所有数值大于t的子序列。注意不是字典序大。

    题解:

    首先特判s比t短或一样长的情况。

    当s比t长时,直接用组合数计算s不以0开头的,长度大于t的所有子序列数量。

    然后再去看s的和t一样长的子序列。

    就是在找s和t的公共子序列,并且一旦某一位s比t大了,就不找了,直接用组合数求此种情况下后面的组合方式,一旦某一位s比t小了,也不找了,直接返回0.

    组合数要预处理。

    #include<iostream>
    #include<cstring>
    #define MOD 998244353
    #define LL long long
    using namespace std;
    char str1[3005],str2[3005];
    LL Cmn[3005][3005]; 
    LL dp[3005][3005]; 
    int qpow(int base,int n){
        int ans=1;
            while(n){
                if(n%2){
                    ans=1LL*ans*base%MOD;
                }
                base=1LL*base*base%MOD;
                n>>=1;
            }
        return ans;
    }
    //void makerev(){
    //    for(int i=1;i<=3000;i++){
    //        rev[i]=qpow(i,MOD-2);
    //    }
    //    return ;
    //}
    void MakeCmn(){
        memset(Cmn,0,sizeof Cmn);
        Cmn[0][0]=1; 
        for(int n=1;n<=3000;n++){
            Cmn[n][0]=1;
            for(int m=1;m<=n;m++){
                Cmn[n][m]=Cmn[n-1][m-1]+Cmn[n-1][m];
                Cmn[n][m]%=MOD;
            }
        }
    //    for(int i=1;i<=3000;i++){
    //        for(int j=1;j<=i;j++){
    //            printf("从%d里取%d个的方案是%d
    ",i,j,Cmn[i][j]);
    //        }
    //    } 
    }
    int l1,l2;
    int main(){
    //    makerev();
        MakeCmn();
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d %d",&l1,&l2);
            scanf("%s %s",str1,str2);
            if(l1<l2){
                printf("0
    ");
                continue;
            }else if(l1==l2){
                for(int i=0;i<l1;i++){
                    if(str1[i]>str2[i])goto B;
                    if(str1[i]<str2[i]){
                        printf("0
    ");
                        goto A;
                    }
                }
                printf("0
    ");
                continue;
                B:;printf("1
    ");
                A:;continue;
            }
            //第一个串比第二个长的情况
            LL ans=0;
            for(int i=l2+1;i<=l1;i++){
                ans+=Cmn[l1][i];
                ans%=MOD;
                for(int j=0;j<l1;j++){
                    if(str1[j]=='0'){
                        ans-=Cmn[l1-1-j][i-1];
                        ans+=MOD;
                        ans%=MOD;
                    }
                }
            }
    //        printf(">:%d
    ",ans);
            dp[0][0] = 1;
            for (int i = 1; i <= l1; i++) {
                dp[i][0] = 1;
                for (int j = 1; j <= min(l2, i); j++) {
                    dp[i][j] = dp[i-1][j];
                    if (str1[i-1] == str2[j-1]) {
                        dp[i][j] = (0LL+dp[i][j] + dp[i-1][j-1]) % MOD;
                    } else if (str1[i-1] > str2[j-1]) {
                        ans = (0LL+ans + dp[i-1][j-1]*Cmn[l1-i][l2-j]) % MOD;
                    }
                }
            }
            
            printf("%d
    ",ans);
        }
        return 0;
    }

    这道题带来的教训:能用dp,不要用dfs,尤其是当两者思维难度差不太多的时候。

  • 相关阅读:
    LeetCode 34. Find First and Last Position of Element in Sorted Array
    LeetCode 875. Koko Eating Bananas
    LeetCode 560. Subarray Sum Equals K
    Vue 3 响应式原理源码解析 All In One
    git rebase 与 git merge 的区别是什么 All In One
    superscript & subscript symbol All In One
    macOS & ifconfig & ipconfig commands All In One
    Chrome UX Report All In One
    TypeScript Utility Types All In One
    TypeScript Type Manipulation All In One
  • 原文地址:https://www.cnblogs.com/isakovsky/p/11290594.html
Copyright © 2020-2023  润新知