题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
递归法求全排列,思路:对字符串a_b_c有
- Perm(a_b_c)= a_Perm(b_c)+ b_Perm(a_c)+ c_Perm(a_b),
- Perm(b_c)=b_Perm(c)+c_Perm(b),
- Perm(a)=a。
其中加号+
表示添加到序列组成的列表里。
难点在于字符序列的重复元素会带来冗余排列要如何避免。例如Perm(a_b_b)=a_Perm(b_b)+2b_Perm(a_b)
产生了两个b_Perm(a_b)
,原因在于先后有两个b被从a_b_b里取出。解决方案:
- 既然原因在于重复的元素先后从序列里取出,那么只要给取出的元素做个记录,再次访问时能够识别出来,跳过即可。
1 | def Perm(s): |
上述实现版本有两个不完美的地方:
- 低效的递归
- 输出的全排列不是字典序的
根据一个字符串序列,有效的计算其下一个字典序列的方法是存在的。这不但能避免低效的递归,而且能按字典序输出全排列。
计算下一个字典序的方法:
- 从后向前找第一对相邻的递增元素,称前一个为替换数
a
,其位置为替换点。 - 再从
a
的后面找一个比它大的最小元素b
,如果有多个元素值与b
相等,就选择最右边的那个(其实,b
是从后边开始的第一个大于a
的元素),交换二者位置。。 - 将替换点之后(不包括替换点)的右子序列的元素反转。
如下版本的代码,接收参数为字符串,是不可变对象,因此把字符串转换为数组之后再操作,最后再变回字符串。有用到冒泡排序将输入转换成其最小字典序。主题部分用了一个循环,从最小字典序依次产生不重复的序列,直到最大字典序。
1 | def Perm(arr): |