题目链接: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 ",ans); } return 0; }