文章目录
面试题38:字符串的排列
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
示例:
输入:s = “abc”
输出:[“abc”,“acb”,“bac”,“bca”,“cab”,“cba”]
限制:
1 <= s 的长度 <= 8
分析:
1. 用递归思路实现全排列:
从第一个字母开始分别与它后面的的字母进行交换。
直接在源字符串上操作,因此需要将交换过的字符换回来;
注意:
- 对结果进行sort字典排序是因为题目中按字典序打印出该字符串中字符的所有排列;
vector<string> &result
:C++对结果用&进行地址传递相当于C里边的指针,就不用重新定义一个指针来传结果值了。- C++的vector中的find函数用法:
#include<vector>
#include<string>
find(r.begin(), r.end(), str)//在数组r中找str这个内容
if(find(r.begin(), r.end(), str) == r.end())//遍历到数组r的末尾都没有找到str这个内容,则if条件为真
cout<<"r中没有str这个值";
递归C代码:
#include<iostream>
using namespace std;
/*思路:
全排列问题的递归实现:从第一个字母开始分别与它后边的数字交换
*/
void Permuate(char* pStr, char* pBegin)
{
if(*pBegin == '\0')
{
cout<<pStr<<endl;;
}
else
{
for(char* pCh=pBegin; *pCh != '\0'; ++pCh)
{
swap(*pCh, *pBegin);
Permuate(pStr, pBegin+1);
//恢复交换
swap(*pBegin, *pCh);
}
}
}
void Permutation(char* pStr)
{
if(pStr == nullptr)
return;
Permuate(pStr, pStr);
}
int main()
{
char str[]="abc";
Permutation(str);
return 0;
}
递归C++代码(AC):
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
void Permutate(string str,vector<string> &result,int begin)
{
if(begin == str.size()-1) // 递归结束条件:索引已经指向str最后一个元素时
{
if(find(result.begin(),result.end(),str) == result.end())
{
// 如果result中不存在str,才添加;避免aa和aa重复添加的情况
result.push_back(str);
}
}
else
{
// 第一次循环i与begin相等,相当于第一个位置自身交换,关键在于之后的循环,
// 之后i != begin,则会交换两个不同位置上的字符,直到begin==str.size()-1,进行输出;
for(int i=begin;i<str.size();++i)
{
swap(str[i],str[begin]);
Permutate(str,result,begin+1);
swap(str[i],str[begin]); // 复位,用以恢复之前字符串顺序,达到第一位依次跟其他位交换的目的
}
}
}
vector<string> Permutation(string str)
{
vector<string> result;
if(str.empty()) return result;
Permutate(str,result,0);
// 此时得到的result中排列并不是字典顺序,可以单独再排下序
sort(result.begin(),result.end());
return result;
}
int main()
{
string s = "abc";
vector<string> x = Permutation(s);
for(int i=0;i<x.size();i++)
cout<<x[i]<<endl;
return 0;
}
2. 用DFS(深度优先搜索)实现全排列:
假设一个数组有N个元素,则他的全排列算法为Q,即Q(0,n)(n = N-1),要想求Q(0,n)只需要 求Q(1,n),求Q(2,n)只需要求Q(3,n)。。。。。。知道求出Q(n,n),我们的算法就求出来了。
DFS思路:
先定义一个数组存放产生的所有状态;
把初始状态放入数组中,设为当前的状态;
扩展当前的状态,产生一个新的状态放入数组中,同时把新产生的状态设为当前状态;
判断当前状态是否和前面的重复,如果重复则回到上一个状态,产生它的另一个状态;
判断当前状态是否为目标状态,如果是目标,则找到了一个解答
DFS实现代码:
//全排列问题
#include<iostream>
#include<vector>
#include<string>
#include<set>
using namespace std;
class Solution {
public:
vector<string> ans; //定义结果数组
vector<char> c_char; //存放字符串的字符数组
vector<string> Permutation(string s) //分成字符数组,然后调用dfs
{
for(int i = 0; i < (int)s.size(); i++) // string 转换成 vector<char> 数组
{
c_char.push_back(s[i]);
}
dfs(0); //从第0个开始固定
return ans;
}
//DFS
void dfs(int x) //dfs只负责搜索
{
if(x == c_char.size() - 1) //递归终止条件:递归固定到第到x==len-1个数时,将字符串入结果数组栈
{
string res(c_char.begin(),c_char.end());
ans.push_back(res);
return;
}
set<char> st; //初始化一个set用于排除重复的字符串
for(int i = x; i < (int)c_char.size(); i++)
{
if(st.count(c_char[i])) //重复则减枝(终止了本次的循环) set.count()查找set中某个键值是否已经出现
continue;
st.insert(c_char[i]); //不重复则继续添加元素
swap(c_char[i], c_char[x]); //将c[i]固定到c[x]位【这里的c_char[i]用来临时放每次固定的值,每次一个字符串固定完成就将其入到结果数组里】
dfs(x + 1); //递归开始固定第x+1个字符
swap(c_char[i], c_char[x]); //回溯:每次res入了结果数组之后才通过return; 来恢复之前的交换
}
}
};
int main()
{
string a = "abc";
Solution x;
vector<string> r = x.Permutation(a);
for(int i=0;i < (int)r.size();i++)
{
cout<<r[i]<<endl;
}
return 0;
}
输出总和leecode上的标准不一样,但也实现了全排列,有点晕:
DFS(AC):待解答~
class Solution {
public:
vector<string> Permutation(string str) {
if(str!="") dfs(str, 0);
return ret;
}
private:
vector<string> ret;
void dfs(string str, int s){
int sz = str.size();
if(s==sz){
ret.push_back(str);
return;
}
for(int i = s; i < sz; ++i){
if(i!=s && str[s]==str[i]) continue;
swap(str[s], str[i]);
dfs(str, s+1);
}
}
};
3. 利用C++中STL提供的next_permutation函数(AC)
- 这个函数包含在#include里
- next_permutation函数语法:
next_permutation(arr, arr+size);
参数:需要全排列的地址区间,arr为数组名;
返回值:生成下一个全排列返回true,否则返回false
总之这个函数就是实现了字符串的全排列,每调用一次生成一个排列,所以要用一个while来不停调用,直到全排列完成
使用next_permutation的函数代码:
需要有头文件:
#include<algorithm>
class Solution {
public:
vector<string> permutation(string s)
{
vector<string> r;
sort(s.begin(), s.end());//先对输入的字符串进行从小到大的排列,然后调用全排列函数
do{
r.push_back(s);
}while(next_permutation(s.begin(), s.end()));
return r;
}
};
总结:
- 全排列就是从第一个字符开始分别与之后的每个字符进行交换(注意题目要求的输出顺序按需进行sort排序);
- DFS的实现就是从第一个字符开始分别向结果字符串中添加不重复的元素;
- 还有一种简便的方法就是使用
next_permutation
函数,没有用过,了解即可。