【2019牛客暑期多校训练营 第五场G题】【subsequence 1】【DP】

题目链接:
https://ac.nowcoder.com/acm/contest/885/G
题意:
给你长度分别为 n , m n,m n,m的字符串 s 1 , s 2 s1, s2 s1,s2 1 ≤ m ≤ n ≤ 3000 1\leq m \leq n\leq 3000 1mn3000,问s1有几个子串严格大于s2.
题解:
f [ j ] [ k ] f[j][k] f[j][k]为当前已经选了 j j j个字符, k表示状态
f [ j ] [ 0 ] f[j][0] f[j][0]表示选 s 1 s1 s1中的 j j j个比 s 2 s2 s2中的 j j j要小的选择个数
f [ j ] [ 1 ] f[j][1] f[j][1]表示选 s 1 s1 s1中的 j j j个和 s 2 s2 s2中的 j j j相等的选择个数
f [ j ] [ 2 ] f[j][2] f[j][2]表示选 s 1 s1 s1中的 j j j个比 s 2 s2 s2中的 j j j要大的选择个数
显然,我们需要的答案是, ∑ i = m n f [ i ] [ 2 ] \displaystyle\sum_{i = m}^nf[i][2] i=mnf[i][2].
现在来看状态怎么转移:
i i i表示当前选到 s 1 s1 s1中第 i i i个数
s 1 [ i ] > s 2 [ j + 1 ] s1[i] > s2[j + 1] s1[i]>s2[j+1]
即新加入的数要比 s 2 s2 s2中对应位置的数大,
那么上一个状态相等的就会变大,原来就大的仍然是大于,原来小的现在还是小
f [ j + 1 ] [ 2 ] + = f [ j ] [ 2 ] + f [ j ] [ 1 ] f[j + 1][2] += f[j][2] + f[j][1] f[j+1][2]+=f[j][2]+f[j][1]
f [ j + 1 ] [ 0 ] + = f [ j ] [ 0 ] f[j + 1][0] += f[j][0] f[j+1][0]+=f[j][0]
s 1 [ i ] = s 2 [ j + 1 ] s1[i] =s2[j + 1] s1[i]=s2[j+1],一样大
那么原来怎么样现在还是怎么样
f [ j + 1 ] [ 2 ] + = f [ j ] [ 2 ] f[j + 1][2] += f[j][2] f[j+1][2]+=f[j][2]
f [ j + 1 ] [ 1 ] + = f [ j ] [ 1 ] f[j + 1][1] += f[j][1] f[j+1][1]+=f[j][1]
f [ j + 1 ] [ 0 ] + = f [ j ] [ 0 ] f[j + 1][0] += f[j][0] f[j+1][0]+=f[j][0]
s 1 [ i ] &lt; s 2 [ j + 1 ] s1[i] &lt;s2[j + 1] s1[i]<s2[j+1],同理可以得到
f [ j + 1 ] [ 0 ] + = f [ j ] [ 0 ] + f [ j ] [ 1 ] f[j + 1][0] += f[j][0] + f[j][1] f[j+1][0]+=f[j][0]+f[j][1]
f [ j + 1 ] [ 2 ] + = f [ j ] [ 2 ] f[j + 1][2] += f[j][2] f[j+1][2]+=f[j][2]
④如果当前已有的数个数已经超过 m m m了,那也是大于 s 2 s2 s2,如123比13大
这个时候不管原来是小于还是等于还是大于,都会归到 f [ j + 1 ] [ 2 ] f[j + 1][2] f[j+1][2],即
f [ j + 1 ] [ 2 ] + = f [ j ] [ 0 ] + f [ j ] [ 1 ] + f [ j ] [ 2 ] f[j + 1][2] += f[j][0] + f[j][1] + f[j][2] f[j+1][2]+=f[j][0]+f[j][1]+f[j][2]
最后需要注意的是,第一位数不能取0,没有意义。
代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define sz sizeof
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 3e3 + 10;
const ll mod = 998244353;

char str[MAX];
int T, n, m;
int s1[MAX], s2[MAX];
ll f[MAX][3];
//设f[j][k]为当前已经选了j个字符, k表示状态
//k=0表示小于
//k=1表示等于
//k=2表示大于
//这里是压掉一维的写法,本来是f[i][j][k],i的意思是当前选到第i位,j和k意思不变,可以先从这个入手
int main() {
	scanf("%d", &T);
	while (T--) {
		memset(f, 0, sz(f));
		scanf("%d%d", &n, &m);
		scanf("%s", str + 1);
		for (int i = 1; i <= n; i++)s1[i] = str[i] - '0';
		scanf("%s", str + 1);
		for (int i = 1; i <= m; i++)s2[i] = str[i] - '0';

		ll ans = 0;
		f[0][1] = 1;
		for (int i = 1; i <= n; i++)
			for (int j = i - 1; j >= 0; j--) {//注意这里一定要从大到小,不然会重复转移
				//之前是空的,新加入的是0就跳过
				if (j == 0 && s1[i] == 0)continue;
				if (j + 1 > m) {//当前串数比s2多
					(f[j + 1][2] += f[j][0] + f[j][1] + f[j][2]) %= mod;
				}
				else {//j + 1 <= m情况
					if (s1[i] > s2[j + 1]) {//新加入的数比s2的要大
					//相等和大于->大于,小于->小于
						(f[j + 1][2] += f[j][2] + f[j][1]) %= mod;
						(f[j + 1][0] += f[j][0]) %= mod;
					}
					else if (s1[i] == s2[j + 1]) {//相等
					//都不变
						(f[j + 1][2] += f[j][2]) %= mod;
						(f[j + 1][1] += f[j][1]) %= mod;
						(f[j + 1][0] += f[j][0]) %= mod;
					}
					else {//要小
					//相等和小于->小于,大于->大于
						(f[j + 1][0] += f[j][0] + f[j][1]) %= mod;
						(f[j + 1][2] += f[j][2]) %= mod;
					}
				}
			}
		//统计答案
		for (int j = m; j <= n; j++)
			(ans += f[j][2]) %= mod;
		cout << ans % mod << endl;
	}

	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值