(File IO): input:ranking.in output:ranking.out
时间限制: 1000 ms 空间限制: 256000 KB 具体限制
题目描述
小
x
x
x有
n
n
n个小姊妹(根据典故,我们假设
n
≤
3000
n≤3000
n≤3000)。他每天都喜欢按不同标准给小姊妹们排(da)序(fen)。今天,他突然对小姊妹们的名字产生了兴趣。他觉得小姊妹的魅力和她们的名字有密切联系,于是他觉得所有有相似的名字的小姊妹必须排在一起。
相似是指,名字的开头一个或若干个连续字母相同。
于是,小x定下了如下规则:
在任何以同样的字母序列开头的名字之间,所有名字开头必须是同样的字母序列。
比如,像
M
A
R
T
H
A
MARTHA
MARTHA和
M
A
R
Y
MARY
MARY这两个名字,它们都以
M
A
R
MAR
MAR开头,所以像
M
A
R
C
O
MARCO
MARCO或
M
A
R
V
I
N
MARVIN
MARVIN这样的名字可以插入这两个名字中间,而像
M
A
Y
MAY
MAY这样的就不行。
显然,按字典序排序是一个合法的排序方案,但它不是唯一的方案。你的任务就是计算出所有合法的方案数。考虑到答案可能很大,输出答案
m
o
d
1000000007
mod 1 000 000 007
mod1000000007。
输入
第一行一个整数
n
n
n,小
x
x
x的小姊妹个数。
第
2
n
+
1
2~n+1
2 n+1行,每行一个字符串,代表这个小姊妹的名字。
输出
一行一个整数,合法的方案数。
样例输入
3
IVO
JASNA
JOSIPA
样例输出
4
数据范围限制
对于
60
60%
60的数据:
3
≤
n
≤
10
3 ≤ n ≤ 10
3≤n≤10。
对于
100
100%
100的数据:
3
≤
n
≤
3000
3 ≤ n ≤ 3000
3≤n≤3000
1
≤
1≤
1≤字符串长度
≤
3000
≤3000
≤3000,并且只含有大写字母。
解题思路
我们可以求出每一个名字的长度,并将其补位 ,然后把所有的字符串排序。 这样相同的前缀的就全部相邻了。 我们依照这个建一颗字典树。 然后在字典树上
d
p
dp
dp。 设
f
[
i
]
f[i]
f[i]表示这个结点以下的点的排列方案数。
F
[
i
]
F[i]
F[i]= *(
i
i
i儿子个数的阶乘)
f
[
0
]
f[0]
f[0]就是答案。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int INF=1000000007;
long long f[4000],maxn;
int n;
string s[400000];
long long dp(int x,int y,int z)//x是现在找到第几个名字,y是还要找几个,z是名字中第几个字母
{
if(z>maxn)//判断是否找完
return f[y-x+1];
char c=s[x][z];//查找相同的字母
int j=x,t=1;
long long ans=1;
for(int i=x+1;i<=y;i++)
{
if(s[i][z]!=c)//出现了一个异类
{
t++;
ans=(ans*dp(j,i-1,z+1))%INF;//递归,从以查找的名字里在再次进行分类,并进行乘法
j=i;
c=s[i][z];
}
}
if(j!=y)
ans=(ans*dp(j,y,z+1))%INF;//特判,如果最后的一段没有弄
ans=(ans*f[t])%INF;//排列组合
return ans;
}
void sort(int l,int r){//手打快排(字符串应该不能直接sort,有其他更简洁办法的留言呀)
if(l>r)return;
int i=l,j=r;
string m=s[r];
string sw;
while(i<=j){
while(s[i]>m)i++;
while(s[j]<m)j--;
if(i<=j){
sw=s[i];
s[i]=s[j];
s[j]=sw;
i++;j--;
}
}
sort(l,j);
sort(i,r);
}
int main()
{
freopen("ranking.in","r",stdin);
freopen("ranking.out","w",stdout);
scanf("%d",&n);
f[0]=1;
for(int i=1;i<=3000;i++)
f[i]=(f[i-1]*i)%1000000007;
for(int i=1;i<=n;i++)
{
cin>>s[i];
long long hh=s[i].size();
maxn=max(maxn,hh);
}//读入,找出一个最长的名字
for(int i=1;i<=n;i++){
for(int j=s[i].size()+1;j<=maxn;j++)
s[i]=s[i]+' ';
}//补位(也方便快排)
sort(1,n);
cout<<dp(1,n,0);
}