题目:
思路:
直观思路是超时的,需要另辟蹊径。
我参考了一下知之可否的算法思路,效率非常高。直接引用ta的话,一看就明白:
思路是这样的,比如当前长度是n,我们知道每个相同的起始元素对应(n-1)!个permutation,也就是(n-1)!个permutation后会换一个起始元素。因此,只要当前的k进行(n-1)!取余,得到的数字就是当前剩余数组的index,如此就可以得到对应的元素。如此递推直到数组中没有元素结束。实现中我们要维护一个数组来记录当前的元素,每次得到一个元素加入结果数组,然后从剩余数组中移除。
————————————————
版权声明:本文为CSDN博主「知之可否」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/gao1440156051/article/details/52318571
需要注意的一点是,如果要用此算法,k的范围应该是0~fact-1,是从0开始算。然而,题目所给的k是从1开始算的,所以要先将k减1。这是理解这个算法的关键,也是决定算法正确性的关键因素。我愣了许久才明白这个问题的。
代码实现:
class Solution {
public:
string getPermutation(int n, int k) {
--k;
string ans(n,0);
if (n <= 0 || k <= -1){
return ans;
}
int fact = 1;
for (int i = 2; i <= n-1; ++i){
fact *= i;
}
vector<int> remain;
for (int i = 0; i < n; ++i){
remain.push_back(i+1);
}
int ans_i = 0;
while (ans_i < ans.size()){
int remain_i;
remain_i = k / fact;
ans[ans_i++] = remain[remain_i] + '0';
remain.erase(remain.begin() + remain_i);
if (n - 1 == 0){
continue;
}
k = k % fact;
fact /= (n - 1);
--n;
}
return ans;
}
};
discuss:
class Solution {
public:
string getPermutation(int n, int k) {
int i, j, f = 1;
string s(n, '0');
for (i = 1; i <= n; ++i){
f *= i; // 所有Permutation Sequence的个数
s[i-1] += i; // 初始化s为12345...n
}
for (i = 0, --k; i < n; ++i){
f /= n - i; // (n-1)!, (n-2)!, (n-3)!, (n-4)!, ... , 1
j = i + k / f; // 计算出s[i]应该的字符的index
char c = s[j]; // 得到该字符
for (; j > i; --j){ // s[i]是当前位置,s[j]是选出来的位置,把s[i]~s[j-1]的元素统统往后移动1个,这样就把s[i]空出来了,然后把c填到s[i]中即可
s[j] = s[j-1];
}
k %= f; // 量级不断减小
s[i] = c;
}
return s;
}
};
参考:https://blog.csdn.net/gao1440156051/article/details/52318571