• 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;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    C# Json数组序列化和反序列总结
    从Excel文件中读取内容
    JS replace()用法实现replaceAll
    JS 缓存
    JS 从HTML页面获取自定义属性值
    根据IP和端口号异步短时间判断服务器是否链接
    时间戳与时间相互转换(13位)(转)
    JS enter事件及数据不完整阻止下一步操作
    JS 检测浏览器中是否安装了特定的插件
    C# Cache 缓存
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910083.html
Copyright © 2020-2023  润新知