2019牛客暑期多校训练营(第五场)G subsequence 1(dp+组合数)

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

题目大意:给一个s串和t串,问s中任意子串(视为正整数),大于t字符串(视为正整数)的总个数。

例如:s:1234, t:13,答案共有9个。

  为14,23,24,34,123,124,134,234,1234。

解题报告:

  1.先求s串中长度与t串相等的子字符串。开一个三维数组,第一维:s串的长度,第二维:t串的长度,第三维:当s串长为i,t串长为j时,0代表前面每一位都相等的方案数,1代表此时s串已经大于t串的方案数。然后就可以开始写转移方程啦。

  2.接着要求出s串中长度比t串长的子字符串(需要用到组合数,可以通过打表解决),即求长为n的字符串中长度大于m的子字符串数量,可以先求当n=5,里面长为3的子字符串个数,会发现是C(5,3),有10个,从m+1枚举到n便可解决。

  3.排除前导零的情况,可以发现,当前位为0时,求的是这一位后面的字符串中长度大于m的字符串,设后面的长度(不包括0这一位)为len,便是C(len,m),从m枚举到n便可解决。

AC代码:

 1 #include<vector>
 2 #include<cstdio>
 3 #include<iostream>
 4 #include<cmath>
 5 #include<queue>
 6 #include<stack>
 7 #include<cmath>
 8 #include<algorithm>
 9 #define numm ch-48
10 #define pd putchar(' ')
11 #define pn putchar('
')
12 #define pb push_back
13 #define fi first
14 #define se second
15 #define fre1 freopen("1.txt","r",stdin)
16 #define fre2 freopen("2.txt","w",stdout)
17 using namespace std;
18 template <typename T>
19 void read(T &res) {
20     bool flag=false;char ch;
21     while(!isdigit(ch=getchar())) (ch=='-')&&(flag=true);
22     for(res=numm;isdigit(ch=getchar());res=(res<<1)+(res<<3)+numm);
23     flag&&(res=-res);
24 }
25 template <typename T>
26 void write(T x) {
27     if(x<0) putchar('-'),x=-x;
28     if(x>9) write(x/10);
29     putchar(x%10+'0');
30 }
31 const int maxn=20010;
32 const int N=60;
33 const int inf=0x3f3f3f3f;
34 const int INF=0x7fffffff;
35 const int mod=998244353;
36 typedef long long ll;
37 char s[3010],t[3010];
38 ll c[3010][3010];
39 int n,m;
40 int dp[3010][3010][2];
41 void init() {
42     c[0][0]=1;
43     for(int i=1;i<=3000;i++) {
44         c[i][0]=1;
45         for(int j=1;j<=i;j++)
46             c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
47     }
48 }
49 ll DP() {
50     for(int i=1;i<=n;i++) {
51         for(int j=1;j<=m;j++) {
52             dp[i][j][0]=dp[i-1][j][0];  ///代表相等的情况
53             dp[i][j][1]=dp[i-1][j][1];  ///代表大于的情况
54             if(j-1==0) {
55                 if(s[i-1]>t[j-1]) ///首字符就开始大于
56                     dp[i][j][1]=(1LL+dp[i][j][1])%mod;
57             }
58             else {
59                 if(s[i-1]>t[j-1])  ///子串现在才s大于t
60                     dp[i][j][1]=(dp[i-1][j-1][0]+dp[i][j][1])%mod;
61                 dp[i][j][1]=(dp[i-1][j-1][1]+dp[i][j][1])%mod;///前面就已经是大于的关系
62             }
63             if(s[i-1]==t[j-1]) {
64                 if(j-1==0)
65                     dp[i][j][0]=(1LL+dp[i][j][0])%mod;
66                 else dp[i][j][0]=(dp[i-1][j-1][0]+dp[i][j][0])%mod;
67             }
68         }
69     }
70     return dp[n][m][1]%mod;
71 }
72 
73 int main()
74 {
75     int _;
76     read(_);
77     init();
78     while(_--) {
79         read(n);read(m);
80         scanf("%s%s",s,t);
81         for(int i=0;i<=m;i++)
82             for(int j=0;j<=n;j++)
83                 dp[i][j][0]=dp[i][j][1]=0;
84         ll ans=DP();   ///1.先算s中和t长度相等的子字符串数量
85 //        write(ans);pn;
86         for(int i=m+1;i<=n;i++) ///2.加上长度为n的字符串中,长度大于等于m+1的子字符串数量
87             ans=(ans+c[n][i])%mod;
88 
89         for(int i=0;i<=n;i++)    ///3.减掉以0开头的子字符串
90             if(s[i]=='0')
91                 for(int j=m;j<=n-i-1;j++)
92                     ans=(ans-c[n-i-1][j]+mod)%mod;
93 
94         write(ans);pn;
95     }
96     return 0;
97 }
代码在这里!
原文地址:https://www.cnblogs.com/wuliking/p/11284680.html