JZOJ 3161. 【GDOI2013模拟2】排序
Time Limits: 1000 ms Memory Limits: 32768 KB
题目
Description
给你 N N N个学生的名字,要求有相同前缀的名字排在一起,具体规则如下:
对于列表中任意两个有相同前缀的名字,排在这两个名字中间的名字也必须拥有相同的前缀。
例如,名字MARTHA和MARY,这两个名字具有相同的前缀MAR,所以MARCO和MARVIN可以排在MARTHA和MARY之间,但MAY却不能。
按照字典序排序肯定满足条件,但这不一定是唯一的方法,你的任务是计算出一共有多少排序方式满足要求。
Input
第一行输入 N ( 3 ≤ N ≤ 3000 ) N(3≤N≤3000) N(3≤N≤3000),表示名字的数量。
接下来 N N N行,每行描述一个长度在 1 1 1到 3000 3000 3000之间的大写英文字母组成的名字。
输入保证名字互不相同。
Output
输出方案数 m o d 1 , 000 , 000 , 007 \mod 1,000,000,007 mod1,000,000,007的值。
Sample Input
输入1:
3
IVO
JASNA
JOSIPA
输入2:
5
MARICA
MARTA
MATO
MARA
MARTINA
输入3:
4
A
AA
AAA
AAAA
Sample Output
输出1:
4
输出2:
24
输出3:
8
Data Constraint
60%的数据满足 N ≤ 10 N≤10 N≤10
题解
- 乍一看,字符串,前缀,马上想到字典树(Trie),仔细一看,发现空间似乎就是故意卡掉这种做法,
- 不过似乎用一些神奇的方式存下这棵树是可以过的,这里就不再介绍。
- 观察发现(题目中也说了),排序后的序列是肯定满足的,那么就先排序,
- 用一个栈来进行操作:
- 一、每次放入一个字符串到栈顶;
- 二、如果栈顶两个串的最长公共前缀长度 T T T大于栈顶第一个串和还未放入栈中的第一个串的最长公共前缀长度,那么就弹栈,否则结束操作;
- 三、把所有顶部所有最长公共前缀长度等于 T T T的全部弹出,若共弹出 x x x个数,总方案数乘上 x ! x! x!;
- 四、接着还要另外放入一个串放回栈顶,该串为上一步弹出的串的最长公共前缀去掉最后一个字符;
- 五、回到第二步。
- 为什么?
- 举个例子:
- 样例2
MARICA
MARTA
MATO
MARA
MARTINA - 排序后是
MARA
MARICA
MARTA
MARTINA
MATO - 先放入前三个串,都不能弹栈
- 放入第四个串后,弹出了第三、四个串, a n s ∗ 2 ! = 2 ans*2!=2 ans∗2!=2.
- 另外放入一个串MAR,
- 发现MAR和未放入的第一个串MATO最长公共前缀长度为 2 2 2,而和前面一个为 3 3 3,那么接着弹栈,
- 可以把目前栈中所有元素弹出, a n s ∗ 3 ! = 12 ans*3!=12 ans∗3!=12.
- 另外放入一个串MA,
- 放入最后一个串后弹栈,
- 全部弹出, a n s ∗ 2 ! = 24 ans*2!=24 ans∗2!=24,
- 再放回一个串M.
- 最终答案就是 24 24 24!
代码
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
#define md 1000000007
struct node
{
char p[3010];
int l;
}s[3010],t[3010];
char z[3010];
int c[3010];
LL f[3010];
int check(int k,int p)
{
int r=(s[k].l)<(s[0].l)?(s[k].l):(s[0].l);
for(int i=1;i<=r;i++)
{
if(s[k].p[i]<s[0].p[i]) return p;
if(s[k].p[i]>s[0].p[i]) return 1-p;
}
if(s[k].l==s[0].l) return 0;
if(s[k].l<s[0].l) return p;
if(s[k].l>s[0].l) return 1-p;
}
void qsort(int l,int r)
{
int x=l,y=r;
s[0]=s[(l+r)/2];
while(x<=y)
{
while(check(x,1)) x++;
while(check(y,0)) y--;
if(x<=y) s[3002]=s[x],s[x]=s[y],s[y]=s[3002],x++,y--;
}
if(x<r) qsort(x,r);
if(y>l) qsort(l,y);
}
int count(node x,node y)
{
int r=(x.l)<(y.l)?(x.l):(y.l);
for(int i=1;i<=r;i++) if(x.p[i]!=y.p[i]) return i-1;
return r;
}
int main()
{
int n,i,j;
scanf("%d\n",&n);
for(i=1;i<=n;i++)
{
scanf("%s\n",z+1);
s[i].l=strlen(z+1);
for(j=1;j<=s[i].l;j++) s[i].p[j]=z[j];
}
qsort(1,n);
t[1]=s[1];
int x=1;
LL ans=1;
f[0]=1;
for(i=1;i<=n;i++) f[i]=f[i-1]*i%md;
for(i=2;i<=n;i++)
{
t[++x]=s[i];
if(x>1)
{
int p=count(t[x],s[i+1]);
if(i+1>n) p=0;
c[x]=count(t[x],t[x-1]);
while(p<c[x])
{
int j=x,ss=2;
while(c[j-1]==c[x]) j--,ss++;
t[j-1].l=c[x];
x=j-1;
ans=ans*f[ss]%md;
c[x]=count(t[x],t[x-1]);
p=count(t[x],s[i+1]);
if(i+1>n) p=0;
}
}
}
ans=ans*f[x]%md;
printf("%lld",ans);
return 0;
}