牛客练习赛60 C—操作集锦

整理的算法模板: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<

 

你可能感兴趣的:(基础算法——DP动态规划)