康托展开与逆康托展开
康托展开据我所理解,应该便是给你一个已知的排列,然后这个排列在全排列中是第几小或者第几大的,一般都是按字典序计算,即为第几小的。康托展开的公式为:X=a[n]*(n-1)!+a[n-1]*(n-2)!+…+a[i]*(i-1)!+…+a[1]*0! 并且,其中a[i]就是按字典序当前没有出现的比a[i]小的字符的个数,最后算出来的X仅仅只是比当前排列小的排列的个数,若是要算第几小,还要加上1,下面举例解释说明:
假如现在有一个排列为 bdca 求它在{a,b,c,d}的全排列中为第几小
那么 比b小的且当前未出现的有a有一个,所以为1,再来看第二个字母d,比d小的有a,b,c,但b已经出现过了,所以为2,以此类推,最终算得,X = 1 * 3! + 2 * 2! + 0 * 1! + 0 * 0! = 10
所以,bdca 为第11个排列在{a,b,c,d}组成的全排列中。
本算法若想联系可以搜索关键字“南阳理工ACM 139我排第几”,下面附上c语言代码实现:
#include <stdio.h>
#define N 12
const int factorial[12] = {1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800};
int function(char str[]);
int main(void)
{
int n;
char string[15];
scanf("%d",&n);
while(n--)
{
scanf("%s",string);
printf("%d\n",function(string)+1);
}
return 0;
}
int function(char str[])
{
int i,j,rev,sum;
rev = sum = 0;
for (i = 0;i < N;i++)
{
rev = 0;
for (j = i+1;j < N;j++)
rev += (str[i] > str[j]);
sum += rev * factorial[N-i-1];
}
return sum;
}
而逆康托展开则可以望文生义,便是康托展开的逆运算,也同样可以代码实现。有兴趣的可以搜索关键字“南阳理工ACM 143我是第几”。下面附上代码:
#include <stdio.h>
#include <string.h>
const int factorial[12] = {1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800};
int main(void)
{
int m,n;
int i,j,k;
int adj[12];
char str[13];
scanf("%d",&n);
while (n--)
{
memset(adj,0,sizeof(adj));
scanf("%d",&m);
--m;
for (i = 11;i >= 0;i--)
{
j = -1;
for (k = 0;k <= m/factorial[i];k++)
while (adj[++j]);
adj[j] = 1;
str[11-i] = 'a' + j;
m = m % factorial[i];
}
str[12] = '\0';
printf("%s\n",str);
}
return 0;
}
以上代码均为C语言