A--1-数据结构(6)--字符串全排列和子序列问题

题目1 字符串全排列问题

两种去重的方式

  1. 在for循环挑首字母的时候
  2. 在得到不去重的全排列之后,最后要答案的时候
#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;
}

猜你喜欢

转载自www.cnblogs.com/jiangxinyu1/p/12407692.html