问题 F: S:排列序数
时间限制: 1 Sec 内存限制: 256 MB题目描述
如果用a b c d这4个字母组成一个串,有4!=24种,如果把它们排个序,每个串都对应一个序号:
abcd 0
abdc 1
acbd 2
acdb 3
adbc 4
adcb 5
bacd 6
badc 7
bcad 8
bcda 9
bdac 10
bdca 11
cabd 12
cadb 13
cbad 14
cbda 15
cdab 16
cdba 17
...
现在有不多于10个两两不同的小写字母,给出它们组成的串,你能求出该串在所有排列中的序号吗?
输入
包含多组测试数据,每组测试数组占一行。
输出
对每组测试数据输出一行,一个整数,表示该串在其字母所有排列生成的串中的序号。注意:最小的序号是0
样例输入
abc
dba
样例输出
0
5
第一次写博客,写的不好,请大家包容。这是我在ACM最近做的一道题目。当时没想出来怎么做,只看出题目的一点点规律,以为就是把abcd这些替换成1234,然后放到数组里面进行排序,但是没弄出来。现在想想弄出来也会超时。(时间限制: 1 Sec)
经过同学的一番指点,知道了规律(规律就是康托展开式!)。比如求adbc的序号就是从a依次到c看其后面有几个比其小的字母,比方说d,d后面有b和c两个字母比它小,所以次序就加上(d后面还有几个字母的阶乘)*2=2!*2=4;次序就是4.同理可得dba就是2 ! *2+1!*1=5;
1.康托展开的解释
康托展开就是一种特殊的哈希函数
把一个整数X展开成如下形式:
X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[2]*1!+a[1]*0!
其中,a为整数,并且0<=a<i,i=1,2,..,n
{1,2,3,4,...,n}表示1,2,3,...,n的排列如 {1,2,3} 按从小到大排列一共6个。123 132 213 231 312 321 。
代表的数字 1 2 3 4 5 6 也就是把10进制数与一个排列对应起来。
他们间的对应关系可由康托展开来找到。
如我想知道321是{1,2,3}中第几个大的数可以这样考虑 :
第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2*2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1*1!=1 所以小于321的{1,2,3}排列数有2*2!+1*1!=5个
。所以321是第6个大的数。 2*2!+1*1!是康托展开。
再举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个 0*3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2 1*2! 。第三位是2小于2的数是1,但1在第一位,所以
有0个数 0*1! ,所以比1324小的排列有0*3!+1*2!+0*1!=2个,1324是第三个大数。
代码如下:#include<iostream>
#include<cstring>
using namespace std;
int main()
{
char str[15];
int q[10]={1},i,j;
for(i=1;i<10;++i)
q[i]=i*q[i-1];
while(cin.getline(str,15))
{
int sum=0,k; //k代表后面比这个字母小的数,sum代表位置
int l=strlen(str);
for(i=0;i<l;++i)
{
k=0;
for(j=i+1;j<l;++j)
if(str[j]<str[i])
{
k++;
}
sum+=q[l-i-1]*k;
}
cout<<sum<<endl;
memset(str,0,sizeof(str));
}
return 0;
}
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
char str[15];
int q[10]={1},i,j;
for(i=1;i<10;++i)
q[i]=i*q[i-1];
while(cin.getline(str,15))
{
int sum=0,k; //k代表后面比这个字母小的数,sum代表位置
int l=strlen(str);
for(i=0;i<l;++i)
{
k=0;
for(j=i+1;j<l;++j)
if(str[j]<str[i])
{
k++;
}
sum+=q[l-i-1]*k;
}
cout<<sum<<endl;
memset(str,0,sizeof(str));
}
return 0;
}