zoukankan      html  css  js  c++  java
  • 天梯---至多删三个字符(DP)

    给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串?

    输入格式:

    输入在一行中给出全部由小写英文字母组成的、长度在区间 [4, 1] 内的字符串。

    输出格式:

    在一行中输出至多删掉其中 3 个字符后不同字符串的个数。

    输入样例:

    ababcc

    输出样例:

    25

    提示:

    删掉 0 个字符得到 "ababcc"。

    删掉 1 个字符得到 "babcc", "aabcc", "abbcc", "abacc" 和 "ababc"。

    删掉 2 个字符得到 "abcc", "bbcc", "bacc", "babc", "aacc", "aabc", "abbc", "abac" 和 "abab"。

    删掉 3 个字符得到 "abc", "bcc", "acc", "bbc", "bac", "bab", "aac", "aab", "abb" 和 "aba"。

    参考于:https://msd.misuland.com/pd/2884250171976189282

    dp[i][j]表示的从前i个字符中删除恰好j个,剩下字符构成的字符串有多少种不同的,在上面样例中就是:

    dp[6][0]=1;

    dp[6][1]=5;

    dp[6][2]=9;

    dp[6][3]=10;

    答案就是这些值累加。

    如何递推dp[i][j],下面这个不完全对的式子还是容易想到的:

    dp[i][j]=dp[i-1][j]+dp[i-1][j-1]

    dp[i-1][j]存储的是前i-1个字符里删除了j个之后能形成的字串个数,那如果我不删除第i个字符,前i个字符就还是只删除了j个字符,dp[i-1][j]的值就可以累加到dp[i][j]上;

    dp[i-1][j-1]存储的时前i-1个字符删除了j-1个后能形成的字串个数,如果我接着把第i个字符删除,就相当于前i个字符删除了j个,那

    dp[i-1][j-1]的值就可以累加到dp[i][j]上;

    可是看似正确,会有重复计算

    比如qwabda 一共6个字符长,这里假设递推式下标从1开始,dp[6][3]=dp[5][2]+dp[5][3];

    而dp[5][2]是前5个里删2个,这肯定包括了删除第4第五个字符bd这种情况,然后再把第6个删除形成dp[6][3]的一员;

    这相当于删除了bda 如右所示 qwabda

    dp[5][3]里肯定包括了删除第3,4,5个字符的情况,如右所示 qwabda;

    而这两种删除方式都会留下 qwa这个字串,说明有重复情况被累加了;

    如何减去重复的次数,每当递推到一个dp[i][j]时,就从第i-1到第1个字符里倒着找有没有等于第i个字符的,找到第一个后停下,假设这个位置是k,那么[k,i-1]和[k+1,i]这两种删除方式得到的剩下的串就是相同的,参考上面红色的部分,而且从[k+1,i-1]这段是两种删除方式中都会删掉的,所以[k,i]这段里有i-k个字符被删掉了,要满足dp[i][j],就还需要从[1,k-1]这段里面删掉(j-(i-k))个字符,而

    dp[k-1][j-(i-k))]就是从前k-1个字符中删除(j-(i-k))个字符能形成多少不同字串,这个值贡献到了dp[i][j]的计算中,而且这个值在计算dp[i][j]累加时被加了两次,现在减掉一倍的它就可以了,然后就break;去递推下一个dp[i][j+1]或者是该dp[i+1][j];

    为什么找到一个相同的后就break呢,比如qwwqabdaga这种,abda这里会需要去重,aga这里也会去重,代码里的循环是从左向右递推的,比dp[i][j]小的任意dp[i-k1][j-k2]都肯定是去过重了,在aga这部分去重时用到的前面的dp[i][j]一定是正确的,不会影响到后面。

     1 #include <bits/stdc++.h>
     2 const int INF=0x3f3f3f3f;
     3 typedef long long LL;
     4 const double eps =1e-8;
     5 const int mod=1e9+7;
     6 const int maxn=1e6+10;
     7 using namespace std;
     8 
     9 LL dp[maxn][5];
    10 string str;
    11 
    12 int main()
    13 {
    14     #ifdef DEBUG
    15     freopen("sample.txt","r",stdin);
    16     #endif
    17     
    18     cin>>str;
    19     int len=str.size();
    20     str=' '+str;
    21     dp[0][0]=1;
    22     for(int i=1;i<len+1;i++)
    23     {
    24         dp[i][0]=1;
    25         for(int j=1;j<=3;j++)
    26         {
    27             dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
    28             for(int k=i-1;k>0&&j>=i-k;k--)
    29             {
    30                 if(str[k]==str[i])
    31                 {
    32                     dp[i][j]-=dp[k-1][j-(i-k)];//减去重复的
    33                     break;
    34                 }
    35             }
    36         }
    37     }
    38     LL ans=dp[len][0]+dp[len][1]+dp[len][2]+dp[len][3];
    39     printf("%lld
    ",ans);
    40     
    41     return 0;
    42 }

    -

  • 相关阅读:
    UVa 106
    UVa 111
    UVa 105
    UVa 104
    UVa 103
    UVa 102
    UVa 101
    UVa 100
    就决定是你了!
    阿姆斯特朗回旋加速喷气式阿姆斯特朗炮
  • 原文地址:https://www.cnblogs.com/jiamian/p/12529224.html
Copyright © 2011-2022 走看看