整理的算法模板:ACM算法模板总结(分类详细版)
链接:https://ac.nowcoder.com/acm/contest/4853/C
来源:牛客网
有一款自走棋有26种操作,每种操作我们都用a,b,c,d,...,x,y,za,b,c,d,...,x,y,za,b,c,d,...,x,y,z的符号来代替.
现在牛牛有一个长度为nnn的操作序列,他现在可以从里面拿出某些操作来组合成一个操作视频, 比如说操作序列是abcdabcdabcd,那么操作视频就有a,b,c,d,ab,ac,ada,b,c,d,ab,ac,ada,b,c,d,ab,ac,ad等(也就是操作序列的子序列).他现在想知道长度为kkk且本质不同的操作视频有多少种.
比如对于abababababab,长度为222且本质不同的结果有ab,aa,ba,bbab,aa,ba,bbab,aa,ba,bb。
考虑到答案可能非常大,你只需要输出在模1e9+71e9+71e9+7意义下的答案就可以了.
第一行两个整数n,kn,kn,k.
第二行一个长度为nnn的字符串,保证只存在小写字母.
一行一个整数表示长度为kkk且本质不同的操作视频的个数.
示例1
3 1
abc
3
思路:DP
状态属性以及注意方程代码中已经注明;关键是去重;
假设一个字符串组成为 sa.....b (s为一个串,a,b是字符);字符 a 可以和 s 中的任何序列构成新的序列,同理b也可以,如果a和b相同,那么两者和s构成的序列就相同,那么就要减去一个,就意味着前面的a不能和s构成新的序列;那么就是减去
f(i-1,j-1) (i为a的位置下标)
// 状态表示:集合:f(i,j)表示前i个字符中长度为j的不同的序列个数
// 属性:SUM;
// 状态计算:f(i,j)=f(i-1,j)+f(i-1,j-1);
// 考虑去重 f(i,j)-=f(pre[s[i]]-1,j-1);
#include
using namespace std;
const int mod=1e9+7;
typedef long long ll;
ll dp[1005][1005];
int pre[30];
char s[1005];
int main()
{
int n,k;
cin >>n>>k;
for(int i=1;i<=n;i++) cin >>s[i];
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
dp[i][0]=1;
for(int j=1;j<=i;j++)
{
dp[i][j]=(dp[i-1][j]+dp[i-1][j-1]+mod)%mod;
if(pre[s[i]-'a']) dp[i][j]-=dp[pre[s[i]-'a']-1][j-1];
}
pre[s[i]-'a']=i;
}
cout <<(dp[n][k]+mod)%mod<