题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
解题思路:
1)固定第一个字符,递归取得首位后面的各种字符串组合;例如abc acb
2)再把第一个字符与后面每一个字符交换,例如:bac cba ,并同样递归获得首位后面的字符串组合;
其中递归的出口,就是只剩一个字符的时候,递归的循环过程,就是从每个子串的第二个字符开始依次与第一个字符交换,(此时剩下后面两个字符)然后继续处理子串。例如得到:bca cab
参考:字符串全排列算法 https://blog.youkuaiyun.com/wzy_1988/article/details/8939140
3)针对字符有重复的情况?
由于全排列就是从第一个数字起,每个数分别与它后面的数字交换,我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这两个数就不交换了。例如abb,第一个数与后面两个数交换得bab,bba。然后abb中第二个数和第三个数相同,就不用交换了。但是对bab,第二个数和第三个数不同,则需要交换,得到bba。由于这里的bba和开始第一个数与第三个数交换的结果相同了,因此这个方法不行。?
换种思维,对abb,第一个数a与第二个数b交换得到bab,然后考虑第一个数与第三个数交换,此时由于第三个数等于第二个数,所以第一个数就不再用与第三个数交换了。再考虑bab,它的第二个数与第三个数交换可以解决bba。此时全排列生成完毕!
这样,我们得到在全排列中去掉重复的规则:
去重的全排列就是从第一个数字起,每个数分别与它后面非重复出现的数字交换。为什么??
参考:https://blog.youkuaiyun.com/hackbuteer1/article/details/7462447
//当i==begin时,也要遍历其后面的所有字符
参考:https://www.cnblogs.com/qqky/p/6918095.html
递归的理解还可参考:https://www.nowcoder.com/questionTerminal/fe6b651b66ae47d7acce78ffdd9a96c7
4)需要对得出的字符串进行排序输出
解法1:递归实现
class Solution {
private:
void swap(char& a, char& b)//交换函数
{
char temp = a;
a = b;
b = temp;
}
void Perm(string str, int begin, int end, vector<string>& vec)
{
if (begin == end)
{
vec.push_back(str);
}
else
{
for (int i = begin; i < end; ++i)
{
if(i!=begin&&str[begin]==str[i])//去重复?
continue;
swap(str[begin], str[i]); //下一次是bac cba 该循环结束
Perm(str, begin + 1, end, vec);//此递归用于实现后续的 acb abc
swap(str[begin], str[i]);
}
}
}
public:
vector<string> Permutation(string str) {
vector<string> vec;
if (str == "")
return vec;
Perm(str, 0, str.length(), vec);
sort(vec.begin(),vec.end());
return vec;
}
};
解法2:迭代实现
字典排序算法
参考:字典序算法 http://www.cnblogs.com/darklights/p/5285598.html
class Solution {
private:
void swap(char& a, char& b)//交换函数
{
char temp = a;
a = b;
b = temp;
}
void sort1(string str,int a,int n)
{
int temp=0;
for(int i=1;i<n-a;++i)
{
for(int j=a+1;j<n-1;++j)
{
if(str[j]>str[j+1])
{
temp=str[j];
str[j]=str[j+1];
str[j+1]=temp;
}
}
}
//return;
}
void Perm(string str, int begin, int end, vector<string>& vec)
{
int n=str.length();
int num=1;
int a=0,b=0;
for(int i=n;i>0;--i)
{
num*=i;//计算有多少种情况,取n的阶层
}
while(num--)
{
//将字符串压入栈中
vec.push_back(str);
//从右往左,找出第一个左边小于右边的数,设为str[a]
for(int i=n-1;i>0;--i)
{
if(str[i-1]<str[i])
{
a=i-1;
break;
}
//从右往左,找出第一个大于str[a]的数,设为str[b]
for(int j=n-1;j>a;--j)
{
if(str[j]>str[a])
{
b=j;
break;
}
}
//交换a和b两个数
swap(str[a], str[b]);
// 从a开始的后面的数从头到尾排序
sort(str.begin()+a+1,str.end());
//sort1(str,a,n);
}
}
}
public:
vector<string> Permutation(string str) {
vector<string> vec;
if (str == "")
return vec;
Perm(str, 0, str.length(), vec);
//sort(vec.begin(),vec.end());
return vec;
}
};
通过率20%,
原因是:
用例:
aa
对应输出应该为:
["aa"]
你的输出为:
["aa","aa"]
没有针对重复数字做说明!
参考:【1】https://www.nowcoder.com/profile/9835143/codeBookDetail?submissionId=12791975
【2】解题笔记(21)——字符串的排列组合问题 https://blog.youkuaiyun.com/wuzhekai1985/article/details/6643127
改进:使得它能够对重复的字符串也能实现全排列
关键的一点是:不用设置循环多少次,只要当查找的左边的数没有小于右边的数时结束循环;
通过的代码:
class Solution {
private:
void swap(char& a, char& b)//交换函数
{
char temp = a;
a = b;
b = temp;
}
void sort1(string str,int a,int n)
{
int temp=0;
for(int i=1;i<n-a;++i)
{
for(int j=a+1;j<n-1;++j)
{
if(str[j]>str[j+1])
{
temp=str[j];
str[j]=str[j+1];
str[j+1]=temp;
}
}
}
//return;
}
void Perm(string str, int begin, int end, vector<string>& vec)
{
int n=str.length();
int num=1;//判断循环结束标志
int a=0,b=0;
//int fexsit=1;// 此部分计算循环次数不够严谨!
//for(int i=n;i>0;--i)
//{
// num*=i;//计算有多少种情况,取n的阶层
// }
while(num==1)
{
//将字符串压入栈中
vec.push_back(str);
//从右往左,找出第一个左边小于右边的数,设为str[a]
int i;
for( i=n-1;i>0;--i)
{
if(str[i-1]<str[i])
{
a=i-1;
break;
}
}
//当左边的数没有小于右边的数时,此时i=0
if(i==0)
{
num=0; //跳出循环标志置零
}
//从右往左,找出第一个大于str[a]的数,设为str[b]
for(int j=n-1;j>a;--j)
{
if(str[j]>str[a])
{
b=j;
break;
}
}
//交换a和b两个数
swap(str[a], str[b]);
// 从a开始的后面的数从头到尾排序
sort(str.begin()+a+1,str.end());
//sort1(str,a,n);
}
}
public:
vector<string> Permutation(string str) {
vector<string> vec;
if (str == "")
return vec;
Perm(str, 0, str.length(), vec);
//sort(vec.begin(),vec.end());
return vec;
}
};