leetcode-permutation sequence

 

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 #include <cmath>
 5 using namespace std;
 6 
 7 class Solution {
 8 public:
 9     string getPermutation(int n, int k) {
10         string s("123456789");
11         string str=s.substr(0,n);
12         string res(n, ' ');//要分配够!否则会出现越界错误。
13         for (int i = 0; i < n; i++)
14         {
15             res[i]=helper(str,k);
16         }
17         return res;
18     }
19     char helper(string &s,int &k)
20     {
21         int tmp = factorial(s.size() - 1);
22         int i = (k-1) / tmp;
23         char c = s[i];
24         //为什么取s[i]呢?因为对于数组s而言,里面的元素虽然每一轮都会删除一个,
25         //但始终保证按从小到大排序(因为数组s最初就是1,2,3,...,n,删掉元素不会打乱其有序性)
26         //因此,在数组s中有且仅有i个元素比s[i]小。(即s[i]是数组中由小到大排序的第i+1个数)
27         //而由于我们当前所计算的c是当前所处理子串的最高位,而且i= (k-1)/tmp意味着最高位的这个数字应该满足其后有且只有i个元素比它小,
28         //由于是最高位,因此其后有i个元素就意味着这是一个在当前数组由小到大排序的第i+1个数。因此s[i]就是我们想找的那个数。
29         s.erase(i,1);
30         k -=tmp*i;//余数
31         return c;
32     }
33 
34     int factorial(int n)
35     {
36         int result=1;
37         if (n == 0) return 1;
38         for (int i = 2; i <= n; i++)
39         {
40             result *= i;
41         }
42         return result;
43     }
44 };
45 
46 int main()
47 {
48     Solution s;
49     cout<<s.getPermutation(5,68)<<endl;
50     return 0;
51 }

 =======================================================

http://blog.sina.com.cn/s/blog_4bf7b6580100l2zs.html

学会使用康托展开后就能方便地解决一系列的问题

一、康托展开

对于一个集合{1,2,3,4,...,n}的从小到大排序(或者从大到小,与从小到大类似,这里只说前者)的全排列显然它有n!项

用自然数1,2,...,n!与之一一对应,这是一种对应法则。


这个法则理解起来并不容易,还是用一些例子来说明吧

例1 {1,2,3,4,5}的全排列,并且已经从小到大排序完毕

(1)找出45231在这个排列中的顺序

比4小的数有3个
比5小的数有4个但4已经在之前出现过了所以是3个
比2小的数有1个
比3小的数有两个但2已经在之前出现过了所以是1个
比1小的数有0个

那么45231在这个排列中的顺序是3*4!+3*3!+1*2!+1*1!+0*0!+1=94

(2)找出35142在这个排序中的顺序

比3小的数有2个
比5小的数有4个但3已经在之前出现过了所以是3个
比1小的数有0个
比4小的数有3个但2,3已经在之前出现过了所以是1个
比2小的数有1个但1已经在之前出现过了所以是0个

那么35142在这个排序中的顺序是2*4!+3*3!+0*2!+1*1!+0*0!+1=68

例2 {1,2,3,4,5,6}的全排列,并且已经从小到大排序完毕

找出423615在这个排序中的顺序

比4小的数有3个
比2小的数有1个
比3小的数有2个但2已经在之前出现过了所以是1个
比6小的数有5个但4,2,3已经在之前出现过了所以是2个
比1小的数有0个
比5小的数有4个但1,2,3,4已经在之前出现过了所以是0个

那么423615在这个排序中的顺序是3*5!+1*4!+1*3!+2*2!+0*1!+0*0!+1=395

看了3个例子大概怎么个运算规则应该是能理解了吧用个一般些的表达式是
对于{1,2,3,...,n}生成的已经从小到大排序好的全排列

x=a[n]*(n-1)!+a[n-1]*(n-2)!+...a[1]*0!
a[m]代表比在第m位的数字小并且没有在第m位之前出现过的数字的个数(以个位数为第1位)

x代表比这个数小的数的个数,所以这个数的顺序就是x+1

注:
1.易知a[1]=0
2.这个版本和百度百科看见的应该有些不同,主要是我没有怎么理解那个式子就自己总结了下

二、康托展开的逆运算
这个内容我没有在网上找到,不过实现起来并不麻烦,主要还是自己的话应该有很多不严谨之处敬请谅解

这个方法还是用例子来说比较好
例1 {1,2,3,4,5}的全排列,并且已经从小到大排序完毕
(1)找出第96个数
首先用96-1得到95
用95去除4! 得到3余23
用23去除3! 得到3余5
用5去除2!得到2余1
用1去除1!得到1余0

有3个数比它小的数是4
所以第一位是4
有3个数比它小的数是4但4已经在之前出现过了所以是5(因为4在之前出现过了所以实际比5小的数是3个)
有2个数比它小的数是3
有1个数比它小的数是2

最后一个数只能是1
所以这个数是45321

(2)找出第16个数
首先用16-1得到15
用15去除4!得到0余15
用15去除3!得到2余3
用3去除2!得到1余1
用1去除1!得到1余0

有0个数比它小的数是1
有2个数比它小的数是3 但由于1已经在之前出现过了所以是4(因为1在之前出现过了所以实际比4小的数是2)
有1个数比它小的数是2 但由于1已经在之前出现过了所以是3(因为1在之前出现过了所以实际比3小的数是1)
有1个数比它小得数是2 但由于1,3,4已经在之前出现过了所以是5(因为1,3,4在之前出现过了所以实际比5小的数是1)

最后一个数只能是2
所以这个数是14352

posted @ 2014-10-17 19:45  Ryan in C++  阅读(228)  评论(0编辑  收藏  举报