牛客网暑期ACM多校训练营(第一场)E 题解

题意:长度为n的字符串,删掉m个字符后有多少种不同的字串。

思路:看到这题应该可以想到dp。n的范围1e5,m只有10,那么我们可以用dp[i][j]表示前i个字符构成的子串删掉j个字符后有多少种不同的串。

我们可以推出方程:dp[i][j]=dp[i-1][i-1]+dp[i-1][j-1].但是如果前面有与a[i]相同的字符a[k] (k<i),并且i与k的位置距离小于等于j,那么就会产生重复。

举个例子cdeaaaemkl

当我们i=7,j=4时,a[3]=a[7],那么如果删掉中间的[eaaa]字串就会变成[cde] (注意i=7,所以不是cdemkl),因为我们已经在前面

i=3时算过了一次[cde]这种情况,所以我们需要dp[7][4]-dp[2][0](仔细想想为什么要减去dp[2][0]而不是dp[3][0]).

下面给出代码,不加读入优化会T,我也不造为什么。

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const ll mod=1e9+7;
ll dp[100005][15];
int pre[100005];
int now[100005];
int a[100005];

int main()
{
    int n,m,k;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        memset(pre,0,sizeof(pre));
        memset(now,0,sizeof(now));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            pre[i]=now[a[i]];
            now[a[i]]=i;
        }
        for(int i=0;i<=m;i++) dp[i][i]=1;

        for(int i=1;i<=n;i++)
        {
            dp[i][0]=1;
            int d = i - pre[i];
            for(int j=1;j<=m&&j<i;j++)
            {
                dp[i][j]=((dp[i-1][j]+dp[i-1][j-1])%mod+mod)%mod;
                if(pre[i]!=0&&d<=j)
                {
                    dp[i][j]=((dp[i][j]-dp[pre[i]-1][j-d])%mod+mod)%mod;
                }
            }
        }
        printf("%lld\n",dp[n][m]);
    }
    return 0;
}
全部评论
你不要用memset,用for循环清空就不会超时了
点赞 回复 分享
发布于 2018-07-20 20:57
(仔细想想为什么要减去dp[2][0]而不是dp[3][0]). 我想不出来T_T
点赞 回复 分享
发布于 2018-07-21 16:16
http://tokitsukaze.live/2018/07/22/2018niuke1.E/ 参考了楼主的做法(楼主牛逼),解释了一下那个为什么(应该是这样理解的吧,不对请告知QAQ)(骗一波访问量)(逃
点赞 回复 分享
发布于 2018-07-22 01:42

相关推荐

球球别再泡了:坏,我单9要了14
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务