JZOJ 3161. 【GDOI2013模拟2】排序(栈)

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(3N3000),表示名字的数量。

接下来 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 N10

题解

  • 乍一看,字符串,前缀,马上想到字典树(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 ans2!=2.
  • 另外放入一个串MAR,
  • 发现MAR和未放入的第一个串MATO最长公共前缀长度为 2 2 2,而和前面一个为 3 3 3,那么接着弹栈,
  • 可以把目前栈中所有元素弹出, a n s ∗ 3 ! = 12 ans*3!=12 ans3!=12.
  • 另外放入一个串MA,
  • 放入最后一个串后弹栈,
  • 全部弹出, a n s ∗ 2 ! = 24 ans*2!=24 ans2!=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);
	for(i=1;i<=n;i++)
	{
		scanf("%s
",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;
}
哈哈哈哈哈哈哈哈哈哈
原文地址:https://www.cnblogs.com/LZA119/p/13910083.html