牛客网暑期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;
}
查看9道真题和解析