【Nowcoder】2019牛客暑期多校训练营(第五场) G-subsequence 1 | 字符串dp

题目链接:https://ac.nowcoder.com/acm/contest/885/G

题目大意:

给出一个长度为 n n n的字符串 s s s,与长度为 m m m的字符串 t t t,询问 s s s中有多少个子序列按照10进制运算后大于 t t t按照十进制运算的结果。

题目思路:

大一时的牛客多校,现在补题,竟然有丝怀念

首先很容易想到的如果长度大于m的子序列,并且首字符不为0,那么一定是可以的

这部分很容易由dp求出,不做多余解释。

所以答案变成了上述求出的部分+长度等于m的 并且满足要求的子序列

那么令

d p [ i ] [ k ] 表 示 s 的 前 i 字 符 与 t 的 前 k 个 字 符 相 等 的 方 案 数 dp[i][k]表示s的前i字符与t的前k个字符相等的方案数 dp[i][k]sitk

很容易得到状态转移方程:

d p [ i ] [ k ] = d p [ i ] [ k ] + d p [ i − 1 ] [ k − 1 ] , s [ i ] = = t [ k ] dp[i][k] = dp[i][k] + dp[i-1][k-1] ,s[i] == t[k] dp[i][k]=dp[i][k]+dp[i1][k1],s[i]==t[k]

这样就可以计算出相等的方案数

如何计算大于的的方案数呢?
那么考虑最终状态,显然最后一定会有一部分相等,一部分大于

所以可以枚举每一个分界点 i i i,每个分界点 i i i都可以作为第 k , { 1 < = k < = m } k,\{1<=k<=m\} k,{ 1<=k<=m}个位置,对于每个位置后面都会有 C ( n − i , m − k ) C(n-i,m-k) C(ni,mk)种选择,所以就可以得出最后答案的贡献了

这是一个题的第二篇题解,当然还有第一篇:大一写的,思维不一样竟然推出了不一样的方程https://blog.csdn.net/qq_43857314/article/details/98212272

Code:

/*** keep hungry and calm CoolGuang!  ***/
/*#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize(3)*/
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define d(x) printf("%lld\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e17;
const ll maxn = 4e5+700;
const int mod= 998244353;
const int up = 1e9;
template<typename T>inline void read(T &a){
    
    char c=getchar();T x=0,f=1;while(!isdigit(c)){
    
    if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){
    
    x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
ll dp[3005][3005];///A的前i个,匹配了B的前j个de'd
ll ta[3005][3005];
ll c[3005][3005];
char s[maxn],t[maxn];
ll cal(ll x,ll y){
    
    
    if(y>x) return 0;
    if(y == 0 || x == y) return c[x][y] = 1;
    if(y == 1) return c[x][y] = x%mod;
    if(~c[x][y]) return c[x][y];
    return c[x][y] = (cal(x-1,y) + cal(x-1,y-1))%mod;
}
int main(){
    
    
    memset(c,-1,sizeof(c));
    int T;scanf("%d",&T);
    while(T--){
    
    
        read(n);read(m);
        scanf("%s%s",s+1,t+1);
        dp[0][0] = 1;
        ll ans = 0;
        for(int i=1;i<=n;i++){
    
    
            for(int k=0;k<=m;k++) dp[i][k] = dp[i-1][k];
            for(int k=1;k<=m;k++){
    
    
                if(s[i] == t[k])
                    dp[i][k] = (dp[i][k] + dp[i-1][k-1])%mod;
                else if(s[i] > t[k])
                   ans  = (ans + dp[i-1][k-1]*cal(n-i,m-k))%mod;
            }
        }
        dp[0][0] = 1;
        for(int i=1;i<=n;i++){
    
    
            for(int k=0;k<=n;k++) dp[i][k] = dp[i-1][k];
            if(s[i] != '0') dp[i][1]++; 
            for(int k=2;k<=n;k++) dp[i][k] = (dp[i][k] + dp[i-1][k-1])%mod;
        }
        for(int i=m+1;i<=n;i++) ans = (ans + dp[n][i])%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

/***
5 7
1 2 3 4 5
1 2
2 3
3 4
4 5

3 1
3 2
2 1 5
1 2
2 1 3
3 1
3 5
****/

猜你喜欢

转载自blog.csdn.net/qq_43857314/article/details/111702676