给出集合 [1,2,3,…,n]
,其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。
说明:
- 给定 n 的范围是 [1, 9]。
- 给定 k 的范围是[1, n!]。
示例 1:
输入: n = 3, k = 3 输出: "213"
示例 2:
输入: n = 4, k = 9 输出: "2314"
今天看到了这道题,同时也学习了一个新东西,那就是康托展开和逆康托展开,同时也在感叹数学的精美和奇妙,下面就这个题说一下逆康托展开的主要思路:
n=4,k=15的时候,我们开始逐步确认每一位的数字应该是多少
- 首先我们确认第1位:k=14,index=k/(n-1)!=2,这就说明比第1位数字小的有2个,那么第一位确认为3,更新k的值,k=k-index*(n-1)!=2
- 接下来确认第2位:index=k/(n-2)!=1,说明除去第一位之后比第二位小的有1个,那么第二位确认为2,k=k-index*(n-2)!=0
- 接下来确认第3位:index=k/(n-3)!=0,说明除去前两位之后没有比第三位小的,那么第三位确认为1,k=k-index*(n-3)!=0
- 接下来确认最后一位:index=k/(n-4)!=0,最后一位确认为4
所以最后的结果就为3214,所以根据这个思路给出下面的AC代码:
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
string getPermutation(int n, int k) {
int fac[]={1,1,2,6,24,120,720,5040,40320,362880};
string result;
string candidates="123456789";
k -=1;
for(int i=n-1;i>=0;--i) {
int index=k/fac[i];
k-=index*fac[i];
result+=candidates[index];
candidates.erase(index,1);//从第index位开始,删除1个字符
}
return result;
}
int main(){
int x,k;
cin>>x>>k;
cout<<getPermutation(x,k);
return 0;
}