一.题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
二.解题思路
1.递归+dfs
深度优先搜索。
要注意本题的输出要求字典序输出。
如何字典序的获得一个字符串的全排列?
我们可以这样想,
假设字符串s长为n,我们已知基于s的2~n位的子字符串的全排列,将第一个字符(假设位a)加到2~n位字符串所有全排列的元素中的开头,我们就可以得到字符串s全排列中所有以a开头的排列。
然后,我们再将字符串s第二个字符作为第一个字符,第一个字符作为第二个字符,那么我们又可以的到以s[2]开头的字符串s的所有全排列。
当我们将所有可能的s字符分别作为开头,原第一个元素与相交换的元素之间的元素顺势移到被交换的元素后面,比如‘abcde'.
我要将c作为开头,就会得到c和bcde的全排列相组合,我们就可以得到s的全排列(注意假设我们已经有字符串s的子串2~n的全排列)。
为什么要按顺序相应的和第一个字符替换,因为题目要求我们按字典序输出。
如果原字符串s是字典序,子字符串的全排列也是字典序,那么通过这种方式,能保证我们的结果也是字典序。
好了,之前是假设2~n的字串的全排列已知,那我们如何求2~n的字串。
这就得到我们用于递归的递推式。
用同样的方法,我们可以得到3~n,4~n的递推。
递归的终止条件就是递归到字符串末尾,也就是空串,看是返回。
详细可以看代码自己消化一下。
递归法是有很多重复运算的。比如算bcde和acde的全排列的时候,都需要计算cde的全排列。当然你可以用空间换时间,用个数据结构把所有中间结果存下来,不过这样子可能内存爆炸噢。
时间复杂度:O(
2.动态规划
目前动态规划只适用于非字典序输出。
和上面的思路类似但是有点小不一样。动态规划是自下而上的。
动态规划中,假设我们有了第1~i位的子串的全陪排列,如何计算1~i+1字串的全排列。
很容易想到,我们把第i+1个字符插入到1~i的所有全排列的每个元素的所有可能插入位置中。
只需要保存前后两个位置的全排列,因此空间复杂度也能接受。
但是为什么说无法字典序输出?
假设前面0~i的全排列是字典序。
因为我是遍历全排列中的所有元素并插入所有可能的位置,即使原全排列的结果是有序,但是插入位置会打乱这种有序状态。
比如说,
[ab,ba],现在我要插入c,假设插入从头到尾,结果应该是[cab,acb,abc,cba,bca,bac]。
假设从尾插到头,结果应该是[abc,acb,cab,bac,bca,cba].
很明显都从有序变成了无序状态。
这是因为当我们把元素插入到某一位的时候,这一位和其他解中该位的字典序比较的大小就变了。
时间复杂度O(N*N*N)
3.递归+回溯
和1差不多的想法。
参考:牛客网回溯
三.源码
# 1
class Solution:
def Permutation(self, ss):
# write code here
if not ss:return []
rst=[]
existed=set()
for i in range(len(ss)):
temp=self.Permutation(ss[:i]+ss[i+1:])
if not temp:return [ss[i]]
for s in temp:
if s not in existed:
rst.append(ss[i]+s)
existed.add(s)
return rst
# 2 不能ac,只能生成非字典全排列
class Solution:
def Permutation(self, ss):
# write code here
if not ss:return []
cur,last=[],['']
for i in range(len(ss)):
existed=set()
for s in last:
for k in range(len(last[0])+1):
new_s=s[:k]+ss[i]+s[k:]
if new_s not in existed:
cur.append(new_s)
existed.add(new_s)
last,cur=cur,[]
return last