Permutation Sequence

描述

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):

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

Given n and k, return the kth permutation sequence.

Note: Given n will be between 1 and 9 inclusive.

分析

首先可以想到一个简单直白的方法,即调用 k-1next_permutation(),从而得到第k个排列。这个方法把前k个排列全部求出来了,比较浪费,时间复杂度是 O(kn),所以会超时。有没有办法直接求第k个排列呢?有!

利用康托编码的思路,假设有n个不重复的元素,第k个排列是a1,a2,a3,...,ana_1, a_2, a_3, …, a_n,那么a1a_1是哪一个位置呢?

我们把a1a_1去掉,那么剩下的排列为a2,a3,...,ana_2, a_3, …, a_n, 共计n-1个元素,n-1个元素共有(n-1)!个排列,于是就可以知道 a1=k/(n1)!a_1 = k / (n-1)!

同理,a2,a3,...,ana_2, a_3, …, a_n 的值推导如下:

k2=k%(n1)!k_2 = k\%(n-1)!

a2=k2/(n2)!a_2 = k_2/(n-2)!

\quad \cdots

kn1=kn2%2!k{n-1} = k{n-2}\%2!

an1=kn1/1!a{n-1} = k{n-1}/1!

an=0a_n = 0

康托编码

  1. // Permutation Sequence
  2. // 康托编码
  3. // 时间复杂度O(n),空间复杂度O(1)
  4. class Solution {
  5. public:
  6. string getPermutation(int n, int k) {
  7. string s(n, '0');
  8. string result;
  9. for (int i = 0; i < n; ++i)
  10. s[i] += i + 1;
  11. return kth_permutation(s, k);
  12. }
  13. private:
  14. int factorial(int n) {
  15. int result = 1;
  16. for (int i = 1; i < n+1; ++i)
  17. result *= i;
  18. return result;
  19. }
  20. // s 已排好序,是第一个排列
  21. string kth_permutation(string &s, int k) {
  22. const int n = s.size();
  23. string result;
  24. int base = factorial(n - 1);
  25. --k; // 康托编码从0开始
  26. for (int i = n - 1; i > 0; k %= base, base /= i, --i) {
  27. auto a = next(s.begin(), k / base);
  28. result.push_back(*a);
  29. s.erase(a);
  30. }
  31. result.push_back(s[0]); // 最后一个
  32. return result;
  33. }
  34. };

相关题目

原文: https://soulmachine.gitbooks.io/algorithm-essentials/content/cpp/linear-list/array/permutation-sequence.html