問題 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;
}