2019牛客暑期多校训练营(第五场)- G subsequence 1

题目链接:https://ac.nowcoder.com/acm/contest/885/G

题意:给定字符串s,t,求s中满足字典序大于t的子序列的个数。

思路:组合数学+dp。当子序列长度大于m时很简单,利用初始化的组合数即可。当子序列长度等于m时类似于数位dp的处理办法,我们用dp[pos][num]表示处理到s中的第pos位时已经选择num个数的可能个数。

   那么当s[pos]>t[num+1]时:

    如果选择s[i]:dp[pos][num]=(dp[pos][num]+C[n-pos][m-num-1])%MOD;

    如果不选:dp[pos][num]=(dp[pos][num]+dfs(pos+1,num))%MOD;

   当num!=m-1&&s[pos]==t[num+1]时(num!=m-1是因为避免相等的情况,相等时只能不选):

    如果选择s[i]:dp[pos][num]=(dp[pos][num]+dfs(pos+1,num+1))%MOD;

    如果不选:dp[pos][num]=(dp[pos][num]+dfs(pos+1,num))%MOD;

   当s[pos]<t[num+1]:

    只能不选:dp[pos][num]=(dp[pos][num]+dfs(pos+1,num))%MOD;

AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
 
typedef long long LL;
const int MOD=998244353;
const int maxn=3005;
 
int T,n,m;
char s[maxn],t[maxn];
LL ans,C[maxn][maxn],dp[maxn][maxn];
 
void init(){
    for(int i = 0; i < maxn;++i){
        C[i][0] = C[i][i] = 1;
        for(int j = 1; j < i;++j){
            C[i][j] = C[i-1][j] + C[i-1][j-1];
            C[i][j] %= MOD;
        }
    }
}
 
LL dfs(int pos,int num){
    if(num==m) return 1;
    if(pos>n) return 0;
    if(dp[pos][num]!=-1) return dp[pos][num];
    LL tmp=0;
    if(s[pos]>t[num+1])
        tmp=(tmp+C[n-pos][m-num-1])%MOD;
    else if(num!=m-1&&s[pos]==t[num+1])
        tmp=(tmp+dfs(pos+1,num+1))%MOD;
    if(n-pos>=m-num)
        tmp=(tmp+dfs(pos+1,num))%MOD;
    return dp[pos][num]=tmp;
}  
 
int main(){
    init();
    scanf("%d",&T);
    while(T--){
        ans=0;
        scanf("%d%d",&n,&m);
        scanf("%s",s+1);
        scanf("%s",t+1);
        for(int i=0;i<=n+1;++i)
            for(int j=0;j<=m;++j)
                dp[i][j]=-1;
        ans=(ans+dfs(1,0));
        for(int i=m+1;i<=n;++i)
            for(int j=1;j<=n-i+1;++j)
                if(s[j]!='0')
                    ans=(ans+C[n-j][i-1])%MOD;
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/FrankChen831X/p/11284065.html