(纪中)1733.【10.5NOIP普及模拟】ranking(ranking.pas/cpp)【数学】【DP】

(File IO): input:ranking.in output:ranking.out
时间限制: 1000 ms 空间限制: 256000 KB 具体限制


题目描述
x x n n 个小姊妹(根据典故,我们假设 n 3000 n≤3000 )。他每天都喜欢按不同标准给小姊妹们排(da)序(fen)。今天,他突然对小姊妹们的名字产生了兴趣。他觉得小姊妹的魅力和她们的名字有密切联系,于是他觉得所有有相似的名字的小姊妹必须排在一起。
相似是指,名字的开头一个或若干个连续字母相同。
于是,小x定下了如下规则:
在任何以同样的字母序列开头的名字之间,所有名字开头必须是同样的字母序列。
比如,像 M A R T H A MARTHA M A R Y MARY 这两个名字,它们都以 M A R MAR 开头,所以像 M A R C O MARCO M A R V I N MARVIN 这样的名字可以插入这两个名字中间,而像 M A Y MAY 这样的就不行。
显然,按字典序排序是一个合法的排序方案,但它不是唯一的方案。你的任务就是计算出所有合法的方案数。考虑到答案可能很大,输出答案 m o d 1000000007 mod 1 000 000 007


输入
第一行一个整数 n n ,小 x x 的小姊妹个数。
2   n + 1 2~n+1 行,每行一个字符串,代表这个小姊妹的名字。

输出
一行一个整数,合法的方案数。


样例输入
3

IVO

JASNA

JOSIPA

样例输出
4


数据范围限制
对于 60 60% 的数据: 3 n 10 3 ≤ n ≤ 10
对于 100 100% 的数据: 3 n 3000 3 ≤ n ≤ 3000
1 1≤ 字符串长度 3000 ≤3000 ,并且只含有大写字母。


解题思路
我们可以求出每一个名字的长度,并将其补位 ,然后把所有的字符串排序。 这样相同的前缀的就全部相邻了。 我们依照这个建一颗字典树。 然后在字典树上 d p dp 。 设 f [ i ] f[i] 表示这个结点以下的点的排列方案数。 F [ i ] F[i] = *( i i 儿子个数的阶乘) f [ 0 ] f[0] 就是答案。

扫描二维码关注公众号,回复: 9358970 查看本文章

代码

#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);
}
发布了73 篇原创文章 · 获赞 5 · 访问量 1823

猜你喜欢

转载自blog.csdn.net/kejin2019/article/details/104171124