60. Permutation Sequence (String; Math)
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.
Note: Given n will be between 1 and 9 inclusive.
思路:
本题不像Permutation,不要求罗列所有permutation,所以没必要用回溯递归。
本题如果用Next Permutation,会导致超时。需要寻找规律。
规律:在n!个排列中,除去第一位,后几位共有(n-1)!个排列,所以第一位的元素总是(n-1)!一组出现的。那么,第k行第一位的值就=nums[(k-1)/(n-1)!]。
依次类推第二位的值=nums[(k2-1)/(n-2)!],其中k2=(k-1)%(n-1)! Note: +1是因为k从1开始计数,而非0
class Solution { public: string getPermutation(int n, int k) { string result = ""; bool hasUsed[n+1]; //whether the number has used in the string for(int i = 0; i<=n; i++) { hasUsed[i] = false; } int dp[n]; //permutation number of a digit with i width = i! dp[0] = 1; dp[1] = 1; for(int i = 2; i< n; i++) { dp[i] = i* dp[i-1]; //用动态规划法求阶乘 } int num; //记录第i位的数字 stringstream ss; string str; for(int i = n; i>0; i--) { num = k/dp[i-1]; if(k%dp[i-1] != 0) num+=1; int counter = 0; int j; for(j = 1; j<=n && counter!=num; j++) { if(!hasUsed[j])counter++; //如果这个数字已经出现,则不计数 if(counter == num) break; } hasUsed[j] = true; ss.clear(); ss<<j; ss>>str; result += str; k = k-(num-1)*dp[i-1]; //该位的数字订了后,也就意味着已经处理一部分permutation number, 要把这部分去掉 } return result; } };
思路II:
首先,不用把每个状态阶乘都存储,可以求出n! 然后每次遍历除以i便可以得到当前循环所需要的阶乘。
其次,不需要flag,但设一个num[]记录还剩余的数字,这样的好处是,原先要做加法、赋值、比较操作,现在update num只需要赋值操作。
class Solution { public: string getPermutation(int n, int k) { string result = ""; vector<int> num; //num记录第i个数字是多少 int factorial = 1; for(int i = 0; i < n; i++){ num.push_back(i+1); //给num赋初值 factorial *= num[i]; //求阶乘(n-1)! } int index; //记录第i位的数字在num[]中的下标 for(int i = n; i > 0; i--) //遍历每一位数字 { factorial /= i; //update factorial index = (k-1)/factorial; //第n位数以(n-1)!为一组,按组出线;index表示第k行(从1开始计数)在第几组(从0开始计数) k = (k-1)%factorial +1; //update k result += ('0'+num[index]); for(int j = index; j+1 < i; j++) //update num { num[j] = num[j+1]; }; } return result; } };