一、题目
The set [1,2,3,…,n]
contains a total of n! unique permutations.
By listing and labeling all of the permutations in order,
We get the following sequence (ie, for n = 3):
"123"
"132"
"213"
"231"
"312"
"321"
Given n and k, return the kth permutation sequence.
二、分析
此题可用暴力枚举,即调用 k - 1 次的 std::next_permutation() 或者是在 LeetCode(31): Next Permutation 所实现的函数,但这样做求出了前 k 个所有排列,会超时,我们只需要求出第 k 个即可。
为了能更清楚地说明接下来的算法,我们先来看对于 n = 3 时,所有排列的特点。
- 可分为 n = 3 组,每组有 (n - 1)! 即 (3 - 1)! 个排列;
- 第 m 组(其中, 1 ≤ m ≤ n)中,第一个数为所有数字升序排列的第 m 个数,如:第 3 组第一个数为 [1, 2, 3] 的第三个数,即3;
- 第 m 组的第一个排列,即总的第 (m - 1) * (n - 1)! + 1 个排列,第一个数之后,为升序排列;
- 第 m 组的最后一个排列,即总的第 m * (n - 1)! 个排列,第一个数之后,为降序排列;
有此规律,我们便可递归求解,即总的 n 个数的第 k 个排列,可以通过确定第一个数之后,求剩下的 (n - 1) 个数的第 k - m * (n - 1)! 个排列,其中,m = floor(k / (n - 1)!)。
三、代码实现
class Solution {
private:
long fact(int n){
if(n == 0){
return 1;
}else{
return n * fact(n - 1);
}
}
void permute(string& str, int start, int k){
if(k == 1 || k == 0){
return;
}
const int len = str.size();
if(k == fact(len - start)){
sort(str.begin() + start, str.end(), greater<char>());
return;
}else{
int tmp = fact(len - start - 1);
int num = k / tmp;
k -= num * tmp;
if(k == 0){
swap(str[start], str[start + num - 1]);
sort(str.begin() + start + 1, str.end(), greater<char>());
}else{
swap(str[start], str[start + num]);
sort(str.begin() + start + 1, str.end());
}
permute(str, start + 1, k);
}
}
public:
string getPermutation(int n, int k) {
string s;
int sum = 0;
s.resize(n);
for_each(s.begin(), s.end(), [&sum](char& c){ c = '0' + (++sum); });
permute(s, 0, k);
return s;
}
};