【问题标题】:Algorithm to generate all permutation by selecting some or all charaters通过选择部分或全部字符生成所有排列的算法
【发布时间】:2010-10-02 07:00:54
【问题描述】:

我需要通过选择一些元素来生成字符串的所有排列。就像我的字符串是 "abc" 输出将是 { a,b,c,ab,ba,ac,ca,bc,cb,abc,acb,bac,bca,cab,cba }。

我想到了一个基本算法,在该算法中,我生成所有可能的“abc”组合,即 {a,b,c,ab,ac,bc,abc},然后将它们全部置换。

那么是否有任何有效的置换算法,通过它我可以生成具有不同大小的所有可能的置换。

我为此编写的代码是:

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <map>
    using namespace std;

    int permuteCount = 1;


    int compare (const void * a, const void * b)
    {
      return ( *(char*)a - *(char*)b);
    }

    void permute(char *str, int start, int end)
    {
        // cout<<"before sort : "<<str;

        // cout<<"after sort : "<<str;
          do
         {
               cout<<permuteCount<<")"<<str<<endl;  
               permuteCount++;
         }while( next_permutation(str+start,str+end) );  
    }

void generateAllCombinations( char* str)
{
     int     n, k, i, j, c;
     n = strlen(str);

     map<string,int> combinationMap;

for( k =1; k<=n; k++)
{  
   char tempStr[20];
   int index =0;
   for (i=0; i<(1<<n); i++) {
        index =0;
        for (j=0,c=0; j<32; j++) if (i & (1<<j)) c++;
        if (c == k) {

        for (j=0;j<32; j++) 
            if (i & (1<<j)) 
               tempStr[ index++] = str[j];          
        tempStr[index] = '\0';
        qsort (tempStr, index, sizeof(char), compare);
        if( combinationMap.find(tempStr) == combinationMap.end() )
        {
        //  cout<<"comb : "<<tempStr<<endl;
        //cout<<"unique comb : \n";
            combinationMap[tempStr] = 1; 
            permute(tempStr,0,k);   
        }  /*
        else
        {
            cout<<"duplicated comb : "<<tempStr<<endl;
        }*/
        }
  }


}
}


    int main () {


            char str[20];
            cin>>str;

            generateAllCombinations(str);

           cin>>str;
    }

我需要使用哈希来避免相同的组合,所以请告诉我如何使这个算法更好。

谢谢, GG

【问题讨论】:

  • 我没有阅读您的代码,但您的口头描述听起来正确:使用 [en.wikipedia.org/wiki/Power_set] 和排列组合。要枚举 幂集,请考虑增加一个二进制数,其中每个“数字”对应于选择输入元素以出现在输出中的次数。对于输入集中的重复元素,“二进制”数的某些“数字”将变为三进制,即该元素的重复计数。
  • 请注意,对于长度为 N 的字符串,在最坏的情况下(如果所有字符都不同)和每个由 L 组成的子集,您将拥有 2^N-1 不同的非空子集字符,您将拥有L! 排列。

标签: c++ algorithm permutation combinations


【解决方案1】:
#include <algorithm>
#include <iostream>
#include <string>

int main() {
  using namespace std;
  string s = "abc";
  do {
    cout << s << '\n'; 
  } while (next_permutation(s.begin(), s.end()));
  return 0;
}

Next_permutation 使用一个常量大小,但您可以添加一个循环来处理不同的大小。或者只是存储在一个集合中,为您消除额外的欺骗:

#include <set>

int main() {
  using namespace std;
  string s = "abc";
  set<string> results;
  do {
    for (int n = 1; n <= s.size(); ++n) {
      results.insert(s.substr(0, n));
    }
  } while (next_permutation(s.begin(), s.end()));
  for (set<string>::const_iterator x = results.begin(); x != results.end(); ++x) {
    cout << *x << '\n';
  }
  return 0;
}

【讨论】:

  • 我不知道该怎么做,你能详细说明一下吗?
  • 我猜这里有一些问题。可以说,对于字符串 “ACBC” 结果:1a2ac3acb4acbc5acc6accb7b8ba9bac10bacc11bc12bca13bcac14bcc15bcca16c17ca18cab19ca bc20cac21cacb22cb23cba24cbac25cbc26cbca27cc28cca29ccab30ccb31ccba但它应该是1a2c3b4ac5ca6ab7ba8bc9cb10cc11bc12cb13abc14acb15bac16bca17cab18cba19acc20cac21cc a22abc23acb24bac25bca26cab27cba28bcc29cbc30ccb31abcc32acbc33accb34bacc35bcac36bc ca37cabc38cacb39cbac40cbca41ccab42ccba SPAN>
  • @GG:啊,我一开始错过了:如果你想遍历所有排列,next_permutation 需要对初始状态进行排序。必要时使用 std::sort。
【解决方案2】:

我不认为你能写出比你已经写的更快的程序。主要问题是输出大小:它的顺序为 n!*2^n(子集数 * 一个子集的平均排列数),对于 10 个不同字符的字符串,它已经是 &gt; 10^9

由于 STL 的 next_permutation 为这么小的字符串增加了非常有限的复杂度,所以您的程序的时间复杂度已经接近 O(output size)

但是你可以让你的程序更简单一些。特别是,for( k =1; k&lt;=n; k++) 循环似乎没有必要:您已经在内部变量c 中计算了子集的大小。因此,只需使用 int k = c 而不是 if (c == k)。 (您还需要考虑空子集的情况:i == 0

编辑
实际上,n == 10(不是~ 10^9)只有 9864100 个输出。尽管如此,我的观点还是一样:你的程序已经为每个输出只浪费了“O(next_permutation)”时间,这非常非常少。

【讨论】:

  • 所以我可以使输出大小变长,因为我用它来打印序列号。对于 int,我可以生成最多 n = 32 的组合
  • @GG 仅打印 10^9 个不同的字符串应该需要将近一小时的时间(以及大约 10GB 的磁盘空间)。是你想要的吗?
  • @nikita 我没有看到任何其他方式?或者我应该设置一个限制,请不要给我大于 9 的数字。
  • @GG 没有办法打印 10^9 个字符串而不打印它们。 (如果您更改要求,例如,如果您决定只想知道这些字符串的数量,那将是另一回事。)因此,我的建议是继续使用您当前的程序:您不会得到更快的解决方案。
  • 谢谢 nikita,我会坚持要求。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-21
  • 1970-01-01
  • 2015-03-19
相关资源
最近更新 更多