线性dp+思维 牛客练习赛60 C题 操作集锦

操作集锦

题目描述

有一款自走棋有26种操作,每种操作我们都用a,b,c,d,…,x,y,z的符号来代替.
现在牛牛有一个长度为n的操作序列,他现在可以从里面拿出某些操作来组合成一个操作视频, 比如说操作序列是abcdabcd,那么操作视频就有a,b,c,d,ab,ac,ad等(也就是操作序列的子序列).他现在想知道长度为k且本质不同的操作视频有多少种.
比如对于abab,长度为2且本质不同的结果有ab,aa,ba,bb。
考虑到答案可能非常大,你只需要输出在模1e9+7意义下的答案就可以了.

输入描述:

第一行两个整数n,k.
第二行一个长度为n的字符串,保证只存在小写字母.

输出描述:

一行一个整数表示长度为k且本质不同的操作视频的个数.


求子序列个数,明显要用到dp,而且可以显然推出dp方程:

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

问题是还有重复的,怎么去重呢?

可以记录每个字母最后一次出现的位置,这个方程类似于前缀和,所以只要减去当前字母的前一次最后出现位置的值就行;

dp[i][j] -=dp[ pre[s[i]-‘a’]-1 ][ j-1];

扫描二维码关注公众号,回复: 10549497 查看本文章

这里要注意长度从0开始;

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=100010;
const int M=2000100;
const LL mod=1e9+7;
LL dp[1100][1100];
char s[1100];
int pre[30];
int main(){
	int n,k;
	cin>>n>>k;
	scanf("%s",s+1);
	dp[0][0]=1;
	for(int i=1;i<=n;i++){//位置 
		for(int j=0;j<=i;j++){//长度 
			dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
			if(pre[s[i]-'a']) dp[i][j]-=dp[pre[s[i]-'a']-1][j-1];
			dp[i][j]%=mod;
		}
		pre[s[i]-'a']=i;
	}
	if(k==0){
		cout<<1<<endl;
		return 0;
	}
	if(dp[n][k]<0) dp[n][k]+=mod;
	cout<<dp[n][k]<<endl;
	return 0;
}
发布了264 篇原创文章 · 获赞 46 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_44291254/article/details/105161258