zoukankan      html  css  js  c++  java
  • 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 }
    代码在这里!
  • 相关阅读:
    最小的K个数
    数组中出现次数超过一半的数字
    符串的排列
    二叉搜索树与双向链表
    复杂链表的复制
    String,StringBuilder,StringBuffer
    二叉树中和为某一值的路径
    二叉搜索树的后序遍历序列
    Java单例模式
    222. Count Complete Tree Nodes
  • 原文地址:https://www.cnblogs.com/wuliking/p/11284680.html
Copyright © 2011-2022 走看看