题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
这道题我最开始的想法是递归,每次处理一个字符,然后将其存在一个字符串里面,当字符串到了指定的长度,就把它放在一个vector里面,就可以了。但是问题来了,字符可以重复,这句话很关键。如果单纯的进行处理,最后会造成大量重复的字符串结果。我的想法是写一个判断是否重复的函数,当这个字符串重复的时候,就不压入vector了。递归的时候要注意回溯,代码如下:
class Solution {
public:
vector<string> vec;
vector<string> Permutation(string str) {
if(str.size()<=0) return vec;
vector<bool> visit(str.size(),false);//记录某个字符是不是已经被使用过
vector<char> rec;
dfs(str,visit,0,rec);
return vec;
}
void dfs(string str, vector<bool> &visit, int length, vector<char> &rec){
if(length >= str.size()){
string tmpstr = "";
for(int i = 0;i<rec.size();i++) tmpstr += rec[i];
if(!findDup(vec, tmpstr)) vec.push_back(tmpstr);//看这个字符串之前是不是出现
return;
}
for(int i = 0;i<str.size();i++){
if(!visit[i]){
visit[i] = true;
rec.push_back(str[i]);
dfs(str,visit,length+1,rec);
visit[i] = false;//注意递归时候的回溯
rec.pop_back();
}
}
}
bool findDup(vector<string> &vec, string tmpstr){//判断这个字符串是不是重复过
for(int i = 0 ;i< vec.size();i++){
if(tmpstr == vec[i]) return true;
}
return false;
}
};
我一直觉得我这个办法不是很好,今天想到了一个更好地办法,字符交换。这样通过一个简单的判断就能避免重复的字符造成重复字符串的结果。
首先上代码:
class Solution {
public:
vector<string> vec;
vector<string> Permutation(string str) {
if(str.size()<=0) return vec;
helper(str,0);
sort(vec.begin(), vec.end());
return vec;
}
void helper(string &str, int i){
if(i>= str.size()){
vec.push_back(str);
return;
}
for(int j = i;j<str.size();j++){
if(j==i || str[j]!=str[i]){
swap(str[i], str[j]);
helper(str,i+1);
swap(str[i],str[j]);
}
}
}
};
我们来看以下这个helper函数。它的思想是用递归,每次交换两个元素。从一个整体的角度来说,求一个字符串的全排列,就是在前面一定长度的字符串长度固定的情况下,任意交换后面字符串的两个元素,最后形成总的字符串全排列。然后helper里面的循环,为什么j要从i开始而不是i+1开始呢?这是因为如果j从i+1开始,那么在for循环里面根本不会进入边界条件。换句话说,比如说“abc”这个序列,我们首先就要把这个序列自己输出,然后再考虑其他的序列组合,而j=i开始循环能保证原封不动的输出。
而str[j]!=str[i]的条件则能避免string中出现两个相同的字符造成的重复,这是因为全排列是两个任意字符交换形成的结果,那么字符一样自然就不需要交换了。