PAT顶级(去重DP计数)——1025 Keep at Most 100 Characters (35 分)

1025 Keep at Most 100 Characters (35 分)(很用心写了!)


解题思路:

如果有真正自己做完并理解1020的那题的同学,做这题应该没有多大难度,思路几乎相同,还更好想一些,就是中间有一些证明稍微绕一点。

  1. 记dp[i][j]为前i个字符选j个可以组成的字符串个数,那么很容易得到状态转移方程:dp[i][j]=dp[i-1][j]+dp[i-1][j-1],对应第i个字符不选和选两个情况。
  2. 同时我们需要考虑这两个情况会不会重复计算?显然是会的,只要在前i-1个字符里面有和第i个字符相同的,那么选了第i个字符的字符串必然是以s[i]结尾,而不选第i个字符的字符串也可能出现以s[i]结尾的,所以我们需要找到之前所有以s[i]结尾的字符串个数,它们都会被重复计算一次。那么前i-1个字符以s[i]结尾的字符串有多少个呢?
  3. 找到最大的k(k<i)使得s[k]=s[i],那么dp[k-1][j-1],就是前i-1个字符中以s[i]结尾的字符串个数。有人可能会说那再前面的与s[i]相等的字符呢,不需要考虑吗?其实不难发现在k之前的情况都包含在k里面了(这其实与前文提到的“所以我们需要找到之前所有以s[i]结尾的字符串个数,它们都会被重复计算一次”是一个道理),因此我们只需要计算dp[k-1][j-1]即可。所以最后再用dp[i][j]减去dp[k-1][j-1]就是正确的dp[i][j]
  4. 设置初始状态(注意每一层的dp[i][0]都是1,空字符串也是合法的一种情况,只是统计答案的时候别算上)
  5. 最后将最后一层的所有情况进行累加即可。

代码:

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mp make_pair
#define pb push_back
#define G 6.67430*1e-11
#define  rd read()
#define pi 3.1415926535
#define pdd pair<double,double>
using namespace std;
const ll mod = 1e9 + 7;
const int MAXN = 30000005;
const int MAX2 = 300005;
inline ll read() {
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch>'9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
int dp[1005][105];
signed main()
{
    string s;
    cin >> s;
    string t = '0' + s;
    dp[0][0] = 1;
    dp[1][0] = 1;
    dp[1][1] = 1;
    for (int i = 2; i < t.size(); i++)
    {
        dp[i][0] = 1;
        for (int j = 1; j <= 100; j++)
        {
            dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1])%mod;
            for (int k = i - 1; k; k--)
            {
                if (t[k] == t[i])
                {
                    dp[i][j] = (dp[i][j]- dp[k - 1][j - 1]+mod)%mod;
                    break;
                }
            }
        }
    }
    ll sum = 0;
    for (int i = 1; i <= 100; i++)
    {
        sum = (sum + dp[s.size()][i])%mod;
    }
    cout << sum;
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值