描述
现在有"abcdefghijkl”12个字符,将其按字典序排列,如果给出任意一种排列,我们能说出这个排列在所有的排列中是第几小的。但是现在我们给出它是第几小,需要你求出它所代表的序列.
输入
第一行有一个整数n(0<n<=10000);
随后有n行,每行是一个整数m,它代表着序列的第几小;
输出
输出一个序列,占一行,代表着第m小的序列。
样例输入
3 1 302715242 260726926
样例输出
abcdefghijkl hgebkflacdji gfkedhjblcia
解题思路:逆康托展开:就是根据某个排列的在所有全排列中的名次来确定这个排列。比如求1234的所有排列中排第20的排列是啥,那么就利用辗转相除法确定康托展开中每一项的系数k,然后每次输出当前未出现过的第k个元素即可。
下面演示一下计算过程:因为康托展开值是统计比第k个排列小前面所有排列的个数,所以求第20个排列对应的康托展开值应减1-->为19。
用19/3!=3...余1,说明首元素排在未出现元素的第3个(从下标0开始算),即首位为4;
用1/2!=0...余1,说明第二个元素排在未出现元素的第0个,即第二个元素为1;
用1/1!=1...余0,说明第三个元素排在未出现元素的第1个,即第三个元素为3;
最后一位自然就是2,所以1234的所有排列中排第20位的排列为4132。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 char str[13];LL n,m,pos,j,k,len,tmp,fac[13];bool flag[13]; 5 LL getfac(int n){//打印阶乘表 6 if(n==0)return 1; 7 LL ans=1; 8 for(int i=1;i<=n;++i)ans*=i; 9 return ans; 10 } 11 int main(){ 12 for(int i=0;i<13;++i)fac[i]=getfac(i);//阶乘表 13 while(cin>>n){ 14 while(n--){ 15 cin>>m;k=0;len=13;m--;//m减掉次序1才是第m个排列的康托展开值 16 memset(flag,false,sizeof(flag));//初始化为未标记状态 17 for(LL i=1;i<len;++i){ 18 tmp=m/fac[len-i-1]; 19 for(j=0;j<len-1;++j){//遍历12个字符,找到还未出现的字母序列中的第tmp个 20 if(!flag[j]){ 21 if(tmp==0)break; 22 tmp--; 23 } 24 } 25 str[k++]='a'+j; 26 flag[j]=true;//标记第tmp个元素已出现 27 m%=fac[len-i-1]; 28 } 29 str[len-1]='