【2020.12.02提高组模拟】球员(player) 题解
题意描述
基本的递推。
①所有运动员姓氏的长度必须不同。
②每个运动员的姓氏必须是长度比其长的所有其他运动员姓氏的连续子串
潜在的球员分成 (N) 类,第(i)类的球员的姓氏恰好有(i)个字母,且每一类恰好有(K)个球员。 有多少种不同的方法选出满足要求的$ N $个球员。答案对(10^9+7)取余。
Solution
先抛开字符串,思考(DP).
用(f[i][j])表示第(i)类运动员中的第(j)个与其前面的(i-1)类运动员有多少种匹配方案。
那么
[f[i][j]=sum_{t=1}^{k}[s[i-1][t]是s[i][j]的一部分]f[i-1][t]\
f[1][j]=1
]
那么我们需要(O(nk))枚举,再(O(k))枚举上一层,再(O(n))判断两个字符串是否为包含关系,总时间复杂度为(O(n^2k^2))。
优化,用(哈希)或者(AC自动机)或(map)即可,时间复杂度取决于你用什么。
(trie)的话貌似会被卡呢(笑)
Code
/*
Name:
Copyright: iee
Author: hsh778205
Date: 02/12/20 10:04
Description: time O(n^2klog(nk)),memory O(n^2k+?)
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#ifdef TH
#define debug printf("Now is %d
",__LINE__);
#else
#define debug
#endif
using namespace std;
inline int read()
{
int x=0,fu=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') fu=-1,ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*fu;
}
int G[55];
template<class T>inline void write(T x)
{
int g=0;
if(x<0) x=-x,putchar('-');
do{G[++g]=x%10;x/=10;}while(x);
for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar('
');
}
#define p 1000000007
map<string,LL>m;
int n,k,i,j;
LL ans;
string str[51][1510],t1,t2;
int main()
{
// freopen("player.in","r",stdin);
// freopen("player.out","w",stdout);
n=read();
k=read();
for(i=1;i<=n;i++)
{
for(j=1;j<=k;j++)
{
cin>>str[i][j];
}
}
for(i=1;i<=k;i++) m[str[1][i]]++;
for(i=2;i<=n;i++)
{
for(j=1;j<=k;j++)
{
t1=str[i][j].substr(0,str[i][j].size()-1);
t2=str[i][j].substr(1);
if(m.find(t1)!=m.end()) m[str[i][j]]=(m[str[i][j]]+m[t1])%p;
if(t1!=t2) if(m.find(t2)!=m.end()) m[str[i][j]]=(m[str[i][j]]+m[t2])%p;
}
}
for(i=1;i<=k;i++)
{
ans=(ans+m[str[n][i]])%p;
m[str[n][i]]=0;
}
write(ans);
return 0;
}