【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;
}
相關文章
相關標籤/搜索