题目描述:
输入一个字符串,
按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
求全排列:
思路如图所示:
还有一个问题要注意,就是如果字符串中有重复的字符串
由于全排列就是从第一个数字起,每个数分别与它后面的数字交换,我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这两个数就不交换 了。例如abb,第一个数与后面两个数交换得bab,bba。然后abb中第二个数和第三个数相同,就不用交换了。但是对bab,第二个数和第三个数不 同,则需要交换,得到bba。由于这里的bba和开始第一个数与第三个数交换的结果相同了,因此这个方法不行。
换种思维,对abb,第一个数a与第二个数b交换得到bab,然后考虑第一个数与第三个数交换,此时由于第三个数等于第二个数,所以第一个数就不再用与第三个数交换了。再考虑bab,它的第二个数与第三个数交换可以解决bba。此时全排列生成完毕!
这样,我们得到在全排列中去掉重复的规则:
去重的全排列就是从第一个数字起,每个数分别与它后面非重复出现的数字交换。
字典序全排列
上面的思路求出了全排列,但不能保证是字典序。得到字典序有两种方式:
- 对全排列结果进行排序
- 对初始字符串排序,然后在递归的过程中将全排列的交换两个元素改为两者之间的元素移位。
排序方法代码:
1 class Solution { 2 public: 3 vector<string> Permutation(string str) { 4 if(str.size() == 0) 5 return vector<string>(); 6 //sort(str.begin(),str.end()); 7 vector<string> ret; 8 Permutation(str, 0, ret); 9 sort(ret.begin(), ret.end()); 10 return ret; 11 } 12 13 void Permutation(string str, int begin, vector<string> &ret){ 14 if(begin == str.size()){ 15 ret.push_back(str); 16 return; 17 } 18 set<char> charset; // 可能有重复元素 19 for(int i=begin; i<str.size(); ++i) 20 if(i==begin || charset.count(str[i]) == 0){ 21 charset.insert(str[i]); 22 swap(str[begin], str[i]); 23 Permutation(str, begin+1, ret); 24 swap(str[begin], str[i]); 25 } 26 } 27 };
不用对结果排序:
class Solution { public: vector<string> Permutation(string str) { if(str.size() == 0) return vector<string>(); sort(str.begin(),str.end()); vector<string> ret; Permutation(str, 0, ret); return ret; } void Permutation(string str, int begin, vector<string> &ret){ if(begin == str.size()){ ret.push_back(str); return; } set<char> charset; // 可能有重复元素 for(int i=begin; i<str.size(); ++i) if(i==begin || charset.count(str[i]) == 0){ charset.insert(str[i]); string str1 = shift(str, begin, i); Permutation(str1, begin+1, ret); str1 = str; } } string shift(string str, int left, int right){ char tmp = str[right]; for(int i=right; i>left; --i) str[i] = str[i-1]; str[left] = tmp; return str; } };