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\n",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\n", i,j); ( ans+=C[n-1-i][j-1] )%=mod; //同上的选定原则 } } } printf("%lld\n",ans); } }