牛客多校第5场 G题 subsequence1

9 篇文章 0 订阅

牛客多校第5场 G题 subsequence1

题意:一个S的串中有多少个比T大的子序列(不连续的),要取模。

题解:纯DP的题目。建立一个三维DP[i][j][3],i,j代表了S的前i项有多少种子序列能匹配T的前j个数字。最后一个维度3,代表有三个矩阵,一个是S的前i项有多少种子序列大于T的前j个数字(2),一个是S的前i项有多少种子序列等于T的前j个数字(1),一个是S的前i项有多少种子序列小于T的前j个数字(0)。
DP过程:
A:当s[i]>s[j]时候,DP[i][j][2] = dp[i-1][j-1][2] +dp[i-1][j-1][1] 。即:当当前s[i]比t[i]大的时候,大于的矩阵加上上一个已经大于的子序列的个数(存在dp[i-1][j-1][2]中),加上上一个已经等于的子序列的个数(存在dp[i-1][j-1][1]中),上一位如果是小于的话,那么不用加,因为加上了还是小于。
B:剩下的2种以此类推。
C:特判0的位置,如果当前为0,且j从第一位开始比较,则是不行的,应为不能前导0。即,子串t[1]!=‘0’;
D:注意DP的起点。

代码:


#include<cstdio>
#include<string>
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
const ll mo = 998244353;
const int ma = 3e3 + 7;
long long dp[ma][ma][3];
char s[ma], t[ma];
int Case, n, m;
int main() {
	cin >> Case;
	while (Case--) {
		cin >> n >> m;
		cin >> s;
		cin >> t;
		for (int i = n; i >= 1; i--)
			s[i] = s[i - 1], t[i] = t[i - 1];
		//dp[0][0][0] = dp[0][0][1] = dp[0][0][2] = 1;
		dp[0][0][1] = 1;
		//dp[1][0][1] = 1;
		int f0=1, f1=1, f2=1;
		f1 = f2 = f0 = 0;
		for (int i = 1; i <= n; i++) {
			dp[i][0][1] =  1;
			
			for (int j = 1; j <= i; j++) {
				dp[i][j][0] = dp[i - 1][j][0];  //小于
				dp[i][j][1] = dp[i - 1][j][1];  //等于
				dp[i][j][2] = dp[i - 1][j][2];  //大于
				if (s[i] == '0'&&j==1)
					continue;
				if (j > m) {
					dp[i][j][2] += dp[i - 1][j - 1][2] + dp[i - 1][j - 1][1] + dp[i - 1][j - 1][0];
					dp[i][j][2] %= mo;
					continue;
				}
				
				if (s[i] > t[j]) {
					dp[i][j][2] += dp[i - 1][j - 1][2] + dp[i - 1][j - 1][1];
					dp[i][j][0] += dp[i - 1][j - 1][0];
				}
				if (s[i] == t[j]) {
					dp[i][j][0] += dp[i - 1][j - 1][0];
					dp[i][j][1] += dp[i - 1][j - 1][1];
					dp[i][j][2] += dp[i - 1][j - 1][2];
				}
				if (s[i] < t[j]) {
					dp[i][j][0] += dp[i - 1][j - 1][1] + dp[i - 1][j - 1][0];
					dp[i][j][2] += dp[i - 1][j - 1][2];
				}
				dp[i][j][0] %= mo;
				dp[i][j][1] %= mo;
				dp[i][j][2] %= mo;
			}
		}
		ll ans = 0;
		for (int i = m; i <= n; i++)
			ans = (ans+dp[n][i][2])%mo;
		cout << ans << endl;
	}
	return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值