给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 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"。
数据到了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 ;
}