题意:给你两个数字字符串s,t,求字符串s的子序列比字符串t大的个数
思路:他的题解上写的就是dp的基础练习题,好像的确是这么回事,既然是dp,那么对于定义的状态不同得到的转移方程就不同,写法自然就不一样。这里给出其中一种dp的解法
首先从 s 串中选的数字长度大于 t 串长度,肯定ok,那么我们枚举第一个数字的位置并且用组合数搞一搞就可以了,接下来我们用dp搞定长度与 t 串相等且大于 t 的数字数量即可
设dp[i][j]表示 s的前i个,匹配 t 的前 j 个的种类数
- if(s[i] == t[j]) dp[i][j] = dp[i -1][j] + dp[i - 1][j - 1]; //等于的话就等于加上与不加上相加
- if(s[i] < t[j]) dp[i][j] = dp[i - 1][j]; //小于的话就不用算进去了,长度相等时一定会小于,同时可以排除前导零
- if(s[i] > s[j]) ans+=dp[i - 1][j - 1] * C[n - i][m - j]. //前面匹配j-1的种类数*后面随便选len2-j个,大于后长度相等时一定大于
最后注意计算长度大于t的字符串数量排除前导零就可以了
Code

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod = 998244353; const int maxn = 3010; int n, m; ll f[maxn][maxn], dp[maxn][maxn]; char s[maxn], t[maxn]; void pre() { f[1][1] = f[1][0] = f[0][0] = 1; for (int i = 2; i <= 3000; i++){ f[i][0] = 1; for (int j = 1; j <= i; j++) f[i][j] = (f[i-1][j-1]+f[i-1][j])%mod; } } int main() { pre(); int T; scanf("%d", &T); while(T--) { scanf("%d %d", &n, &m); scanf("%s %s", s + 1, t + 1); for(int i = 0; i <= n; i++) dp[i][0] = 1; ll ans = 0; //选择当前位数等于t的序列 for(int i = 1; i <= n; i++) for(int j = 1; j <= min(m, i); j++) { dp[i][j] = dp[i-1][j]; //s[i]<t[j]的情况 if(s[i]==t[j]) dp[i][j] = (dp[i][j] + dp[i-1][j-1])%mod; if(s[i]>t[j]) ans = (ans + dp[i-1][j-1]*f[n-i][m-j])%mod; } //选择当前位数大于t的序列 for(int i = 1; i <= n; i++) { if(s[i] == '0') continue; for(int j = m; j <= n-i; j++) ans = (ans + f[n-i][j]) % mod; //i是枚举第一个数,j是枚举后面取几个 } printf("%lld ", ans); } return 0; }