2019牛客多校训练营第五场补题

G题:subsequence

题意:给定两个由数字字符组成的字符串s , t ,计算将 s 的子序列看成正整数后比 t 大的子序列的数量。

分析:s 的子序列长度大于 t 的一定比 t 大(排除0开头的),通过组合数预处理就能够求得,那么重点算s子序列长度等于t 的有几个是大于t 的。这里显然要用dp的做法,用s、t从右往左的位置标记dp的状态,   dp[ i ][ j ] 表示状态:前 i 个字符串s和前 j 个 字符串t 的最大个数。对于每个状态,它一定包含上一个s 的位置的状态,再根据新的首项是否和 t 的首项相同再加上多了的数量,如果大于,就用选定原理,都选定就加个选定头部的组合数,如果相同也可以都选定加上都是上一位的记录过的dp值。

#include<cstdio>
using namespace std;

typedef long long ll;
const int maxn=3007;
const int mod=998244353; 
ll C[maxn][maxn];
ll dp[maxn][maxn];
char s[maxn],t[maxn];

void init_C(){
    C[0][0]=1;
    for(int i=1; i<maxn; i++){
        C[i][0]=1;
        for(int j=1; j<=maxn; j++){
            C[i][j] = ( C[i-1][j]+C[i-1][j-1] )%mod;
        }
    }
}
int main(){
    init_C();
    int T;
    scanf("%d",&T);
    int n,m;
    while(T--){
        scanf("%d%d",&n,&m);
        scanf("%s%s",s,t);
        for(int i=0; i<=n; i++){
            for(int j=0; j<=m; j++){
                dp[i][j]=0;
            }
        }
        ll ans=0;
        //线性递推的二维dp
        for(int i=m-1; i>=0; i--){
            for(int j=n-1; j>=0; j--){
                ( dp[j][i] += dp[j+1][i] )%=mod;    //保底的
                if(s[j] == t[i]) (dp[j][i] += dp[j+1][i+1]) %= mod;   //可往上加的机会
                else if( s[j]>t[i] ) (dp[j][i] += C[n-1-j][m-1-i]);     //选定思想,选中这一位 这是C直接算而不是dp递推,寓意野蛮生长
            }
        }
        ans+=dp[0][0];
//        printf("ans=%lld
",ans);
        
        for(int i=n-1; i>=0; i--){
            if(s[i]!='0'){
                for(int j=n-i; j>=m+1; j--){
                    // printf("%d %d
", i,j);
                    ( ans+=C[n-1-i][j-1] )%=mod;        //同上的选定原则 
                }
            }
        }
        printf("%lld
",ans);
    }    
}
View Code
原文地址:https://www.cnblogs.com/-Zzz-/p/11532889.html