组合数+dp

  • 给出一个小写字母字符串 T,长度为 nnn。
  • 求有多少长度为 m(m≤105)m (m\leq10^5)m(m≤105) 的小写字母字符串 S,满足 T 是 S 的一个子序列。
  • 我们从合法的新串下手,考虑制定出一种方法来从中找出原串。

  • 对这种方法的要求是:按照这种方法 找出的新串 的下标 必须唯一。

  • 一种合理的匹配方式是:

    • 假如原串是:abcabcabc
    • 假如新串是:aaabbbcccaaabbbccaaccaaabbbcccaaabbbccaaccaaabbbcccaaabbbccaacc
    • 匹配应该是:aaabbbcccaa[a]bb[b]ccaac[c]aaabbbcccaa[a]bb[b]ccaac[c]aaabbbcccaa[a]bb[b]ccaac[c]
    • 匹配规则是:对于某个被匹配到的字符 chchch 开始,从它右边第一个开始,到它右边下一个被匹配的字符左边第一个为止,不得出现跟 chchch 相同的字符。
    • 能发现,按照这种方法 找出的新串 的下标是唯一的。
    • 总结一下这种匹配方式:
      • 我们知道,由子序列 SSS 构造原序列 TTT,问方案数这类问题,就是要保证,
      • 如果你在 TTT 确定 TTT 的第 iii 个字符 tit_iti​,匹配的是 SSS 中的第 ppp 个字符 sps_psp​,
      • 并且你在 TTT 确定 TTT 的第 jjj 个字符 tjt_jtj​,匹配的是 SSS 中的第 p+1p+1p+1 个字符 sp+1s_{p+1}sp+1​,
      • 除了 ti=spt_i=s_pti​=sp​,tj=sp+1t_j=s_{p+1}tj​=sp+1​ 以外,还要保证,ti+1...tj−1t_{i+1}...t_{j-1}ti+1​...tj−1​ 的所有字符,都不包含 tit_iti​。
  • 现在考虑按照这种匹配思路,从原串开始,构造新串。

  • void Sol()
    {
        dp[0][0] = 1;
        for (int i=1; i<=m; i++)    //从你决定开始匹配原串第一个字符之前,放什么都行,每一个位置有26种放法。
        {
            dp[i][0] = dp[i-1][0] * 26 % MOD;
        }
         
        for (int i=1; i<=m; i++)//枚举新串
        {
            for (int j=1; j<=min(i, n); j++)//枚举原串
            {
                //对于新串的第 i 个字符,有两种情况:
                //一种是第i个字符匹配上原串的第j个字符
                //一种是第i个字符在前i-1个已经匹配上原串的第j个字符的情况下,有25种放法。
                dp[i][j] += (j-1<0?0:dp[i-1][j-1]) + dp[i-1][j] * 25 % MOD, dp[i][j] %= MOD;
            }
        }
        printf("%lld\n",dp[m][n]);

    链接:登录—专业IT笔试面试备考平台_牛客网
    来源:牛客网

    Link doesn't remember bbb, so he wonders the number of possible sequences bbb.
     

    A bracket sequence is valid if it satisfies any of the following conditions:

  • Its length is 000.
  • It can be represented as (A)(A)(A), where AAA is a valid bracket sequences.
  • It can be represented as ABABAB, where AAA and BBB are both valid bracket sequence.

  • A sequence aaa is a subsequence of a sequence bbb if aaa can be obtained from bbb by deletion of several (possibly, zero or all) elements.

  • #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<string>
    using namespace std;
    typedef long long ll;
    const int N=205,mod=1e9+7;
    ll dp[N][N][N];
    void solve()
    {
        int n,m;cin>>n>>m;
        string s;
        cin>>s;
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=m;j++)
            {
                for(int k=0;k<=m;k++) dp[i][j][k]=0;
            }
        }
        dp[0][0][0]=1;
        for(int i=1;i<=m;i++)
        {
            for(int j=0;j<=i;j++) 
            {
                dp[0][i][j]+=(j-1<0?0:dp[0][i-1][j-1])+dp[0][i-1][j+1];
                dp[0][i][j]%=mod;
            }
        }
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=min(i,n);j++)
            {
                for(int k=0;k<=i;k++)
                {
                    int d=-1+2*(s[j-1]=='(');
                    if(k-d>=0&&k-d<=m) dp[j][i][k]+=dp[j-1][i-1][k-d];
                    if(k+d<=m&&k+d>=0) dp[j][i][k]+=dp[j][i-1][k+d];
                    dp[j][i][k]%=mod;
                }
            }
        }
        cout<<dp[n][m][0]<<endl;
    }
    int main()
    {
        int t;cin>>t;
        while(t--)
        {
            solve();
        }
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值