L3-020 至多删三个字符 (30 分) 线性dp

时间:2023-03-09 14:58:43
L3-020 至多删三个字符 (30 分)  线性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"。

作者: 曹鹏
单位: Google
时间限制: 400 ms
内存限制: 64 MB

数据到了106  很明显用dp来做

dp[i][j]表示  前i个字符 删除j个字符的 种类

1基础的状态转移

也就是拆与不拆:

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

2判重

有些重复的加了多次 要减回去

例如一个字符串cd abna xy,你删除abn和删除bna后得到的字符串都是cdaxy。(这个区间长度为3)

这时候就要去重了,根据上面那个例子可以发现对于一个字符s[i],如果在i之前存在一个x使得s[x]=s[i],那么删除[x,i-1]间的字符 和删除[x+1,i]间的字符其实是重复的,等价的,那么d[i][j]就要减去d[x-1][j-(i-x)],减去删除这段造成的重复串。

#include<bits/stdc++.h>
using namespace std;
//input by bxd
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i>=(b);i--)
#define RI(n) scanf("%d",&(n))
#define RII(n,m) scanf("%d%d",&n,&m)
#define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
#define RS(s) scanf("%s",s);
#define LL long long
#define pb push_back
#define fi first
#define REP(i,N) for(int i=0;i<(N);i++)
#define CLR(A,v) memset(A,v,sizeof A)
///////////////////////////////////
#define inf 0x3f3f3f3f
#define N 10000000+5
LL dp[N][];
char s[N];
int pos[N];
int main()
{
RS(s+);
int len=strlen(s+);
dp[][]=;
rep(i,,len)
{
dp[i][]=;
int ch=s[i]-'a';
int d=pos[ch];
pos[ch]=i;
rep(j,,)
{
dp[i][j]+=dp[i-][j]+dp[i-][j-];
if(d&&j-i+d>=)
dp[i][j]-=dp[d-][j-i+d];
}
}
printf("%lld",dp[len][]+dp[len][]+dp[len][]+dp[len][] );
return ;
}