avatar

目录
康托展开

康托展开

定义即公式

康托展开表示的是全排列到自然数的映射,康托展开的本质即为某一全排列在整个全排列序列中的顺序。即1 3 2在由1,2,3三个数组成的全排列中的顺序为2。同时此项展开是一个可逆的过程,存在正向运算以及逆向运算。公式为:

X= a[n] * (n-1)! + a[n-1]*(n-2)!+ … +a[i] * (i-1)! + … + a[1] * 0!

其中a[i]为整数,并且 0<= a[i] < i, 1<= i <=n.
a[i]:设x为一串数字或字符串中第i个元素,a[i]即表示x之后的元素中比x小的元素的个数。
例如序列: 3 2 1
X=2 * 2!+1 * 1!+ 0 * 0!=5;

用途

由康托展开的公式可知:n位(0~n-1)全排列后,其康托展开唯一且最大约为n!,因此可以由更小的空间来储存这些排列。由公式可将X逆推出对应的全排列。因此康托展开多用于构建哈希表时的空间压缩。

例题代码

现在有”abcdefghijkl”12个字符,将其所有的排列中按字典序排列,给出任意一种排列,说出这个排列在所有的排列中是第几小的?
输入

每行输入一行字符串,保证是a~l这12个字符的某种排列
EOF结束

输出

输出一个整数,代表这个排列排在第几位。
样例输入:
abcdefghijkl
abcdefghiklj
gfkedhjblcia

样例输出1:
0
3
260726925

代码:

c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
typedef long long ll;
ll ans;
string s;
int factorial(int i)
{
if(i==0||i==1) return i;
else
{
return i*factorial(i-1);
}
}
int main()
{
while(cin>>s){
ans=0;
for(int i=0;i<s.size();i++)
{
int tmp=0;
for(int j=i+1;j<s.size();j++)
{
if(s[j]<s[i])
{
tmp++;
}
}
ans+=factorial(s.size()-i-1)*tmp;
}
cout<<ans<<endl;
}
}

逆康托公式:即给出序号,输出排列:
代码:

c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <bits/stdc++.h>
using namespace std;
int factor(int k){
if(k == 0 || k == 1) return 1;
return k * factor(k-1);
}
int ans[105], book[105];
int main(){
int n, k, m, key, x, j;
while(cin >> n >> m){
memset(book, 0, sizeof book);
--n;
for(int i = 1; i <=m;++i){
k = factor(m-i);
key = n/k+1;
n %= k;
j = 1;
while(key){
if(!book[j]) --key;
++j;
}
ans[i] = j-1;
book[j-1] = 1;
}
for(int i = 1; i <= m; ++i)
printf("%d", ans[i]);
printf("\n");
}
return 0;
}
文章作者: Liang Shuo
文章链接: http://yoursite.com/2020/02/10/%E5%BA%B7%E6%89%98%E5%B1%95%E5%BC%80/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 L·S
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论