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

2 篇文章 0 订阅
1 篇文章 0 订阅

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

You are given two strings s and t composed by digits (characters '0' ∼\sim∼ '9'). The length of s is n and the length of t is m. The first character of both s and t aren't '0'.

Please calculate the number of valid subsequences of s that are larger than t if viewed as positive integers. A subsequence is valid if and only if its first character is not '0'.

Two subsequences are different if they are composed of different locations in the original string. For example, string "1223" has 2 different subsequences "23".
Because the answer may be huge, please output the answer modulo 998244353.

输入描述:

The first line contains one integer T, indicating that there are T tests.

Each test consists of 3 lines.

The first line of each test contains two integers n and m, denoting the length of strings s and t.

The second line of each test contains the string s.

The third line of each test contains the string t.

* 1≤m≤n≤30001 \le m \le n \le 30001≤m≤n≤3000.

* sum of n in all tests ≤3000\le 3000≤3000.

* the first character of both s and t aren't '0'.

输出描述:

For each test, output one integer in a line representing the answer modulo 998244353.

示例1

输入

复制

3
4 2
1234
13
4 2
1034
13
4 1
1111
2

输出

复制

9
6
11

说明

For the last test, there are 6 subsequences "11", 4 subsequcnes "111" and 1 subsequence "1111" that are valid, so the answer is 11.

题目链接:

链接:https://ac.nowcoder.com/acm/contest/885/G?&headNav=acm

题意:

给你两个字符串s、t,让你求s所有子串转化成整数的值大于t转换为整数的值的个数。

题解:

首先两种情况,

  • s字串长度大于t一定符合,枚举第一个字符位置,后面挨着取就好,这是组合数学。
  • 然后第二种就是和t位数相等的情况,这里要用到dp来做。

dp部分如何解决呢,定义

dp[i][j]:s长度为j的后缀有多少长为i的子序列大于t长度为i的后缀个数。首先肯定是    dp[i][j]+=dp[i][j-1]

如果 s[n-j+1]>t[m-i+1],即s长为j的后缀的第一个字符大于t长为i的后缀的第一个字符,那么s在后面的j-1个字符中挑出i-1个也可。

如果 s[n-j+1]=t[m-i+1],那么dp[i-1][j-1]的就行了。

代码:

#include<cstdio>
const int MAX_N = 3005;
const int MOD = 998244353;
int n, m;
char s[MAX_N], t[MAX_N];
long long C[MAX_N][MAX_N];
void ADD(long long &x, long long v) {
    x += v;
    if(x >= MOD) x -= MOD;
}
void pre() {//组合数初始化 
    for(int i = 0;i < MAX_N; i++) {
        C[i][0] = 1;
        for(int j = 1;j <= i; j++) {
            C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
            if(C[i][j] >= MOD) C[i][j] -= MOD;
        }
    }

}
long long dp[MAX_N][MAX_N];
void solve(){
    long long an = 0;
    for(int i = n - 1; i >= 0; i--) {//这里先把长于t的所有组合相加 
        if(s[i] != '0') {
            for(int j = m; j <= n - i - 1; j++) {
                ADD(an, C[n - i - 1][j]);
            }
        }
    }
    for(int j = 0; j <= n; j++) {
        for(int i = 0; i <= m; i++) {
            dp[j][i] = 0;
        }
    }
    for(int i = m - 1; i >= 0; i--) {
        for(int j = n - 1; j >= 0; j--) {
            ADD(dp[j][i], dp[j + 1][i]);
            if(s[j] > t[i]) {//位数一样,但s当前位》t当前位,加上s后所有组合 
                ADD(dp[j][i], C[n - j - 1][m - i - 1]);
            }
            else if(s[j] == t[i]) {//位数一样,s当前位==t当前位,直接加上上次的,继续判断后一位 
                ADD(dp[j][i], dp[j + 1][i + 1]);
            }
        }
    }
    ADD(an, dp[0][0]);
    printf("%lld\n", an);
}
int main() {
    pre();
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        scanf("%s%s", s, t);
        solve();
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值