ACM题目之排列序数

问题 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;





}






















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值