此问题来源于leetcode第60题。
对于一个序列123,我们称123,132,213,231,312,321是它的全排列。
离散数学有一种从后向前搜索替换的方法生成下一个全排列。这种方法持续k次就可以找到第k个。
然而效率感人。
或者有一种从前向后依次交换的方法生成全排列,实际上这些方法本质上都一样,通过交换某一对数字来生成下一个排列。
但是我们可以通过一种类似于字典序生成的方法构造出第k个。
首先考察上边的序列312,这是以123这三个数字构造出的第5个排列。
我们在高中学过,对于一个排列_ _ _第一个空位可以放置3、2、1三个数字,第二个可以放置两个,第三个空位只能放置一个。
因此这叫排列的种数———n!
1 2 3 三个数字在数组中的 有序位置 分别是第0,1,2位。
我们现在看看k是怎么来的。
312为什么排第5位(序号为4)
我们可以理解为2*(2!)+0*(1!)+0*(1!)
这样看有点不太明显。
我们这次用题目的示例:
输入: n = 4, k = 9 输出: "2314"
数字组为:1234,求序号为8(k=9)的排列
那么答案是2314,
那就是1*(3!)+1*(2!)+0*(1!)+0*(0!)=1*6+1*2+0*1+0*1=8
巧合吗?
当然不是。
上面的连加等式第一项:我选择了2,那么2在1234序号为1,剩下三个都在我后面,排列数为3!
第二项:我选择了3,在134中序号为1(2被用过了),剩下两个在我后面,排列数为2!
。。。
懂了吗?这就是构造的方法。
然后我们以数学的角度分析一下这个连加等式为什么正确。
当我从0~n-1个有序数字中选择了第i大的i,那么相当于n个_位置的第一个被选择了。如果_里是0,1,2....i-1的话,那么就有
i*(n-1)!个数字比i______(_有n-1个)小。
所以依次推理,就能得到最终的结果。
而且针对这个题目来说。从剩下数字中选择一个第i大的数字代价很小,因此,可以直接用数组来处理。
这道题答案代码如下:
class Solution:
def getPermutation(self, n: int, k: int) -> str:
arr=[str(i) for i in range(1,n+1)]
ans=[]
fac=[1]
for x in range(1,n):
fac.append(fac[-1]*x)#生成一系列阶乘数值
k-=1
for _ in range(n):
f=fac.pop()
i=k//f
k=k-f*i
ans.append(arr.pop(i))
return ''.join(ans)