【2020.12.02提升组模拟】球员(player)

题目

题目描述

老师们已经知道学生喜欢睡觉,Soaring是这项记录保持者。他只会在吃饭或玩FIFA20时才会醒来。所以,他常常作关于足球的梦,在他最近的一次梦中,他发现本身成了皇家马德里足球俱乐部的总经理。spa

他的工做是挑选N名球员争取在下个赛季战胜巴塞罗那队,可是董事会有两个特殊的要求。具体以下:code

①全部运动员姓氏的长度必须不一样。字符串

②每一个运动员的姓氏必须是长度比其长的全部其余运动员姓氏的连续子串io

为了让工做变得简单,Soaring将潜在的球员分红N类,第i类的球员的姓氏刚好有i个字母,且每一类刚好有K个球员。class

Soaring想知道有多少种不一样的方法选出知足要求的N个球员。答案对(10^9^+7)取余。方法

题解

题目大意:有\(n\)种不一样长度的字符串,每种字符串有\(k\)个,第\(i\)中字符串的长度是\(i\),如今要从\(n\)种字符串里每种选一个,使得选出的字符串都是全部长度比其长的字符串的连续字串,问有多少种选择方案,对\(10^9+7\)取模di

22%

暴力从每种里选择,再暴力判断是否合法时间

时间复杂度\(O(k^nn^4)\),预计得分22co

100%

首先咱们知道,若\(a\)\(b\)的连续字串,\(b\)\(c\)的连续子串,那么\(a\)必定是\(c\)的连续字串字符

那么判断时就能够只判断\(i\)\(i+1\)的关系,而\(i\)\(i+1\)的长度只相差1,说明想要是连续子串,要么是末尾空一个字母,要么开头空一个字母

考虑\(dp\),设\(f[i][j]\)表示到了第\(i\)种,第\(i\)种选择第\(j\)个的方案数,那么枚举\(u\),若\(u\)\(j\)的子串,那么转移:\(f[i][j]+=f[i-1][u]\)

答案是\(\sum_{i=1}^kf[n][i]\)

Code

#include<cstdio>
#define ll long long
#define mod 1000000007
#define N 55
#define K 1505
using namespace std;
int n,m;
ll sq,sh,ans,a[N][K][N],f[N][K];
char s[N];
ll mi(int x)
{
	ll res=1;
	for (int i=1;i<=x;++i)
		res=res*26%mod;
	return res;
}
int main()
{
	freopen("player.in","r",stdin);
	freopen("player.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i)
	{
		for (int j=1;j<=m;++j)
		{
			scanf("%s",s+1);
			for (int k=1;k<=i;++k)
				a[i][j][k]=(a[i][j][k-1]*26%mod+s[k]-'a')%mod;
		}
	}
	for (int i=1;i<=m;++i)
		f[1][i]=1;
	for (int i=2;i<=n;++i)
		for (int j=1;j<=m;++j)
		{
			sq=a[i][j][i-1];//当前字符串的1~i-1位
			sh=(a[i][j][i]-mi(i-1)*a[i][j][1]%mod+mod)%mod;//当前字符串的2~i位
			for (int k=1;k<=m;++k)
				if (a[i-1][k][i-1]==sq||a[i-1][k][i-1]==sh/*判断是不是连续子串*/) f[i][j]=(f[i][j]+f[i-1][k])%mod;
		}
	for (int i=1;i<=m;++i)
		ans=(ans+f[n][i])%mod;
	printf("%lld\n",ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
相关文章
相关标签/搜索