题目1 字符串全排列问题
两种去重的方式
- 在for循环挑首字母的时候
- 在得到不去重的全排列之后,最后要答案的时候
#include <iostream>
#include <string>
#include <vector>
#include <set>
using namespace std;
vector<string> permutation(string s);
void process(string &s, vector<string>& ans, string path);
vector<string> permutation(string s)
{
vector<string> ans;
if (s.empty())
{
return ans;
}
process(s, ans, " ");
return ans;
}
/*
@param1 string s : 待选的字符集合
@param2 ans: 最后的全排列
@param3 path: 路径
*/
void process(string &s, vector<string>& ans, string path)
{
// base case : string 里面没有可供选择的字符了,就把拼接好的path返回
if (s.empty())
{
ans.push_back(path);
return;
}
set<string> picked ;
// 遍历s里面的每个字符
// 表示的是,分别先选择出s里面的每个字符
for (int i = 0; i < s.size(); ++i)
{
// 当前已将挑过的字符都放在picked里
// 如果这个字符已经挑过了,就跳过去了
if ( ! picked.count (s.substr(i,1)) )
{
// 选择当前字符,加到path上,得到 pick
string pick = path + s.substr(i, 1);
picked.insert(s.substr(i, 1));
// 得到下一次选择的字符集合 next
// s 集合不能改啊,for循环还没结束呢
string next(s);
next.erase(i, 1);
// 在选择了一个字符的情况下,再递归调用process,选择下一个字符
process(next, ans, pick);
}
}
}
int main()
{
string ss = "aabc";
// 调用全排列的函数
vector<string> ans = permutation(ss);
cout << ans.size() << endl;
for (auto i : ans)
{
cout << i << endl;
}
return 0;
}
class Solution {
public:
vector<string> permutation(string s) {
vector<string> res;
string path;
set <string> picked;
if (s.empty())
{
return res;
}
process( res , s , path , picked);
return res;
}
// @param1 vector<string>& ans 存储全排列的字符串
// @param2 string& sets 可选的字符串
// @param3 string& path 路径
// @param4 set <string>& picked 去重
void process( vector<string>& ans , const string& sets , string& path , set <string>& picked )
{
// base case : 可选的字符串没了
if ( sets.size() == 0 )
{
// 重复了,就直接返回
if ( picked.count(path) )
{
return ;
}
ans.push_back(path);
picked.insert(path);
return ;
}
// sets里每位置的字符做path[0],分别递归
for ( int i = 0 ; i < sets.size(); ++i)
{
// 对于每个开头,都是一个新的path, 因此不能累加
string path_i = path + sets.substr( i ,1 );
// 对于每个开头,sets都不能变, 因此要复制一下
string next_sets (sets);
next_sets.erase(i,1);
process( ans , next_sets , path_i ,picked );
}
}
};
题目2 打印字符串的全部子序列
典型的从左向右的递归
在每个位置决定要还是不要!
#include <iostream>
#include <set>
#include <vector>
using namespace std;
void process(string str, int index, string path, vector<string> &ans);
vector<string> allSubseq(string str);
vector<string> allSubseq(string str)
{
vector<string> ans ;
if( str.size() == 0 )
{
ans.push_back(" ");
return ans;
}
// 调用递归辅助函数
process(str , 0 , " " ,ans );
return ans;
}
// str[0...index-1]的沿途决定(要或是不要),用string path记录
// 所有的子序列都放到 ans 中
void process( string str , int index , string path, vector<string>& ans )
{
// base_case : 当遍历完当前容器,把 path 加入到 ans 里
if( index == str.size() ){
ans.push_back(path);
}else{
// 当前位置的字符不加入路径->处理下一个位置的字符
process(str,index+1,path,ans);
// 当前位置的字符加入路径->处理下一个位置的字符
process(str, index + 1, path + str[index], ans);
}
}
int main()
{
string ss = "abc";
vector<string> ans = allSubseq(ss);
cout << ans.size() << endl;
for (auto i : ans)
{
cout << i << endl;
}
return 0;
}