![e070cb0995ac57cb0ff41cad2d6f3c5e.png](https://i-blog.csdnimg.cn/blog_migrate/aef128d647106f50cdf9d0003b5e0b5c.jpeg)
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
解题思路及代码
这个题目可以简单地抽象为求一个字符串的全排列。
那全排列通过什么方法可以较好的求出来呢?
我们可以采用递归的方法不断固定某个位置上的字符来完成。
比如求 "ABC" 的全排列:
![9a83efa4082313d77b7419d14a3e79f5.png](https://i-blog.csdnimg.cn/blog_migrate/2348c9c91b700d56963f4775753bbca5.png)
接着我们思考一个问题:在固定某个位置上字符的时候,如何找到该位置上所有可能的情况?(比如对于 ABC 来说,我们要固定第一个位置上的字符,那么有 A、B、C 三种情况,接着我们在 A 固定于第一个位置上之后,再固定第二个位置上的字符,这时我们有 B、C 两种选择,其他的同理。)
我们可以通过交换来获得所有可能的情况。比如第一个位置上的字符,假如 A 和 A 交换,就相当于第一个位置上固定了 A;假如 A 和 B 交换,B来到第一个位置,就相当于第一个位置上固定了 B;假如 A 和 C 交换,C来到第一个位置,就相当于第一个位置上固定了 C。递归的过程中都是同理。
此外,因为可能有字符重复,我们得到的全排列自然也会有重复,那么我们可以用 set
(集合)来去重,并且还可以达到按照字母顺序排序的目的。
代码如下:
class Solution {
public:
void perm(int pos, string s, set<string> &ret) {
if (pos+1 == s.length()) {
ret.insert(s);
return;
}
// for循环和swap的含义:对于“ABC”,
// 第一次'A' 与 'A'交换,字符串为"ABC", pos为0, 相当于固定'A'
// 第二次'A' 与 'B'交换,字符串为"BAC", pos为0, 相当于固定'B'
// 第三次'A' 与 'C'交换,字符串为"CBA", pos为0, 相当于固定'C'
for (int i = pos; i < s.length(); ++i) {
swap(s[pos], s[i]);
perm(pos+1, s, ret);
swap(s[pos], s[i]);
// 回溯的原因:比如第二次交换后是"BAC",需要回溯到"ABC"
// 然后进行第三次交换,才能得到"CBA"
}
}
vector<string> Permutation(string s) {
if (s.empty()) return {};
set<string> ret;
perm(0, s, ret);
return vector<string>({ret.begin(), ret.end()}); //将集合中的字符串转成字符串数组
}
};