将一批变量“abcd ...”分成两批并具有所有可能的组合。 (递归)

时间:2016-01-26 11:14:03

标签: c++ string algorithm recursion combinations

我有两个组(A)和(B)以及一些变量abcde ...(> 2)来分组,至少一个变量在组(A)中,一个在组中(B)。该程序应打印出所有组合。输出的顺序并不重要。

对于3个变量“abc”结果应为:

A  | B
-------
ac | b
ab | c
bc | a
a  | bc
b  | ac
c  | ab

我写的程序是:

#include <string>
#include <stdio.h>

using namespace std;

string gencombinations(string , string );
string removeCharsFromString( string , string);

int main(){
    string result;
    string a = "";
    string b = "";
    string batch = "abc";

    a = batch;

    cout << "batch \ta = {"; cout << a; cout << "},\t batch b = {"; cout << b; cout << "}\t"; cout << endl;
    result = gencombinations(a, b);
    //cout << result << endl;

    return 0;
}

string gencombinations(string a, string b){

    string _tmp;
    string _tmp2;

    for(int i = 0; i < a.size(); i++ ) {

        _tmp = a.at(i+1);
        b = _tmp+b;

        cout << "remove \t {"; cout << _tmp; cout << "},\t\t from a = {"; cout << a; cout << "}\t"; cout << endl;
        a= removeCharsFromString( a, _tmp );
        cout << "batch \ta = {"; cout << a; cout << "},\t batch b = {"; cout << b; cout << "}\t"; cout << endl;

        if(a.size() > 1)
            _tmp2 = gencombinations(a, b);
        if(_tmp2 != "") return _tmp2;

        b = removeCharsFromString( b, _tmp );

    }

    return _tmp2;
}

string removeCharsFromString( string str, string charsToRemove ) {

    for (int i = 0; i < charsToRemove.length(); ++i ) {
        //cout << "charsToRemove  = {"; cout << charsToRemove.at(i); cout << "},\t From String = {"; cout << str; cout << "}\t"; cout << endl;
        std::string::size_type s = str.find(charsToRemove.at(i));
        if (s != std::string::npos)
            str.erase(i+1, 1);
    }
    return str;
}

给出了输出:

batch   a = {abc},       batch b = {}
remove   {b},            from a = {abc}
batch   a = {ac},        batch b = {b}
remove   {c},            from a = {ac}
batch   a = {a},         batch b = {cb}

然后休息。 (开头的批次B中没有变量没问题。)

摆脱out_of_range(来自@Paul的答案)我得到:

string gencombinations(string a, string b){

    string _tmp;
    string _tmp2;
    string _resul;


    for(int i = 0; i < a.size(); i++ ) {
        _tmp = a.at(i);

        b = _tmp+b;

        if(a.size() > 1)
            a= removeCharsFromString( a, _tmp );

        cout << "batch \ta = {"; cout << a; cout << "},\t batch b = {"; cout << b; cout << "}\t"; cout << endl;

        if(a.size() > 1){
            cout << "recursion" << endl;
            _tmp2 = gencombinations(a, b);
        }

        if(_tmp2 != "") {
            return _tmp2;
            cout << "_tmp2 is empty" << endl;
        }

        b = removeCharsFromString( b, _tmp );
        a = _tmp;
    }
    return _tmp2;
}

string removeCharsFromString( string str, string charsToRemove ){
    for (int i = 0; i < charsToRemove.length(); ++i ) {
        std::string::size_type s = str.find(charsToRemove.at(i));
        if (s != std::string::npos)
            str.erase(i, 1);
    }
    return str;
}

输出:

batch   a = {bcd},       batch b = {a}
recursion
batch   a = {cd},        batch b = {ba}
recursion
batch   a = {d},         batch b = {cba}

如何正确递归?

2 个答案:

答案 0 :(得分:3)

这一行:

  for(int i = 0; i < a.size(); i++ ) {
      _tmp = a.at(i+1);

包含逻辑错误。与任何其他类型的数组或向量一样,字符串在c ++中基于0。在某些时候,代码尝试从数组中读取字符串结尾后的第一个char。此时程序将抛出一个out_of_range - 异常(只需用try-catch包装该行来自己观察)。

至于你的代码:我不太了解tmp2的目的。代码中没有明确创建tmp2的行,因此它总是&#34;&#34;,与输入无关。

但实际上有更简单的解决方案,而不是在ab的任意时间移动每个角色,这也会产生重复。相反,您可以使用以下简化之一:

使用0和1的排列

使用0和1的排列,其中0表示将char放在a中,1表示将char放在b中:

void gencombinations(string allchars){
    for(unsigned int permute = 0 ; permute < (1 << allchars.size()) ; permute++){
        string a, b;

        for(unsigned int bit = 0 ; bit < allchars.size() ; bit++)
            if(permute & (1 << bit))
                a += allchars[bit];
            else
                b += allchars[bit];

        cout << "batch: a = {" + a + "}, b: {" + b + "}" << endl;
    }
}

请注意,此解决方案仅适用于特定大小的输入字符串。 permute的bitcount确定输入字符串的最大长度。例如。 32位的数据类型允许最大长度为32的输入字符串。对于大于64的输入字母表,需要解决此基本解决方案。

使用递归调用和分发

使用递归方法调用和fork,其中一个调用将char添加到a,将一个调用添加到b

gencombinations(string chars , string a , string b){
    if(chars.size()){
        char c = chars[0];
        chars.erase(0 , 1);

        gencombinations(chars , a + c , b);
        gencombinations(chars , a , b + c);
    }else
        cout << "batch: a = {" + a + "}, b = {" + b + "}";
}

答案 1 :(得分:2)

您不必删除任何内容。只需继续从批处理中构建组字符串ab,直到批处理已经过量。

当然,您必须为批处理中的每个字母考虑两种情况:它要么进入A组,要么进入B组。当您递归时,您必须为每种情况分叉递归。您还必须从批处理中删除当前字母,但这很容易,因为它是字符串中的第一个字母:

#include <string>
#include <iostream>

using namespace std;

void combo(string batch, string a, string b)
{
    if (batch.size() == 0) {
        if (a.size() && b.size()) {
            cout << a << " :: " << b << "\n";
        }
    } else {
        combo(batch.substr(1), a + batch[0], b);
        combo(batch.substr(1), a, b + batch[0]);
    }
}

int main()
{
    combo("abc", "", "");

    return 0;
}

此解决方案只是在递归的底层打印字符串。您也可以将它们附加到字符串流或字符串。