【问题标题】:Permutation of String letters: How to remove repeated permutations?字符串字母的排列:如何去除重复排列?
【发布时间】:2011-08-02 19:57:41
【问题描述】:

这是一个打印字符串字符排列的标准函数:

void permute(char *a, int i, int n)
{
   int j;
   if (i == n)
     printf("%s\n", a);
   else
   {
        for (j = i; j < n; j++) //check till end of string
       {
          swap((a+i), (a+j));
          permute(a, i+1, n);
          swap((a+i), (a+j)); //backtrack
       }
   }
} 

void swap (char *x, char *y)
{
    char temp;
    temp = *x;
    *x = *y;
    *y = temp;
}

它工作正常,但有一个问题,它还会打印一些重复的排列,例如:

如果字符串是“AAB”

输出是:

AAB
ABA
AAB
ABA
BAA
BAA

这也有 3 个重复条目。

有办法防止这种情况发生吗?

--

谢谢

阿洛克克。

【问题讨论】:

  • 正在使用 std::set 不需要的开销?
  • 听起来像是功课。如果是的话,你应该这样标记它。
  • 先生,这不是家庭作业,我只是在研究一些标准算法,我遇到了这个问题。还要感谢 std::set 因为我在 c++ 中不太好所以不知道。

标签: c++ c algorithm permutation


【解决方案1】:

记下您之前交换的字符:

 char was[256];
 /*
 for(j = 0; j <= 255; j++)
    was[j] = 0;
 */
 bzero(was, 256);
 for (j = i; j <= n; j++)
 {
    if (!was[*(a+j)]) {
      swap((a+i), (a+j));
      permute(a, i+1, n);
      swap((a+i), (a+j)); //backtrack
      was[*(a+j)] = 1;
    }
 }

这必须是迄今为止参赛作品中最快的一个,在“AAAABBBCCD”(100 个循环)上进行了一些基准测试:

native C             - real    0m0.547s
STL next_permutation - real    0m2.141s

【讨论】:

  • 你确定你是在比较同类吗?去掉打印后,让 STL 版本就地修改一个 char 数组,你的版本还快吗?
  • 我不会删除打印,因为我不知道编译器会做什么优化。你可以用printf替换stream out,你是对的,这确实有很大的不同。另外,like vs like:我的代码只是 temporary 修改了 char 数组,最后它恢复了原始状态。
  • @Kumar:当然。你没看到逻辑吗?您的代码给出了重复项,因为它为每个位置使用了所有重复的字符。
  • 我看到了逻辑,但是使用你的这段代码并没有给我正确的结果。我可能做错了什么,但你确定我不需要更改代码的任何其他部分
  • 是否需要第二次交换?
【解决方案2】:

标准库有你需要的:

#include <algorithm>
#include <iostream>
#include <ostream>
#include <string>
using namespace std;

void print_all_permutations(const string& s)
{
    string s1 = s;
    sort(s1.begin(), s1.end()); 
    do {
        cout << s1 << endl;
    } while (next_permutation(s1.begin(), s1.end()));
}

int main()
{
    print_all_permutations("AAB");
}

结果:

$ ./a.out
AAB
ABA
BAA

【讨论】:

    【解决方案3】:

    另一种方法可能是:

    1. 对数组进行预排序。

    2. 这将确保所有重复项现在都是连续的。

    3. 因此,我们只需要查看我们修复的前一个元素(并置换其他元素)

    4. 如果当前元素与前一个元素相同,则不要置换。

    【讨论】:

      【解决方案4】:

      我会这样做:首先,我生成字符“组”(即AABBBC 产生两个组:(AA) and (BBB) and (C)

      首先,我们将AA 的所有分布迭代到n 字符上。对于找到的每个分布,我们将BBB 的所有分布迭代到n-2 剩余字符(未被A 占用)。对于涉及As 和Bs 的每个分布,我们将C 的所有分布迭代到剩余的空闲字符位置。

      【讨论】:

      • 我真的很喜欢这个,因为你根本不生成重复。
      • 这也是我的想法。但是,实际实现它可能会变得有点麻烦。
      • 不,我认为您甚至可以就地执行此操作,并且非常有效,如果您传递一个空插槽数组(如果您填充一个插槽,则将最后一个数组条目移动到相应的插槽在索引数组中)。
      【解决方案5】:

      您可以使用std::set 来确保结果的唯一性。那就是如果它是 C++(因为你这样标记它)。

      否则 - 手动检查结果列表并删除重复项。

      当然,您必须保存结果并对其进行后处理,而不是像现在这样立即打印。

      【讨论】:

      • 谢谢,这会有所帮助,但我希望代码也能在 c 中工作,因为我也用 c 标记了它。所以另一种方式对我来说很好。
      • @Kumar - 是 C 还是 C++?使用 C 编译器编译时,C++ 代码可能无法工作,C 代码可能无法使用 C++ 编译器。编写 C 代码不会使其成为 C++,请自行决定使用哪种语言。
      【解决方案6】:

      如果您只是认为这是一个需要存储所有排列以供将来使用的问题,那将非常简单。

      所以你会有一个排列的字符串数组。

      现在考虑一个新问题,这也是一个标准问题,您需要从数组中删除重复项。

      希望对你有帮助。

      【讨论】:

      • 这将创建n! 排列,然后做一个非平凡的过滤器
      【解决方案7】:

      @Kumar,我认为您想要的是以下内容:

      #include <stdio.h>
      #include <string.h>
      
      /* print all unique permutations of some text. */
      void permute(int offset, int* offsets, const char* text, int text_size)
      {
          int i;
      
          if (offset < text_size) {
                  char c;
                  int j;
      
                  /* iterate over all possible digit offsets. */
                  for (i=0; i < text_size; i++) {
                          c=text[i];
                          /* ignore if an offset further left points to our
                             location or to the right, with an identical digit.
                             This avoids duplicates. */
                          for (j=0; j < offset; j++) {
                                  if ((offsets[j] >= i) &&
                                      (text[offsets[j]] == c)) {
                                          break;
                                  }
                          }
      
                          /* nothing found. */
                          if (j == offset) {
                                  /* remember current offset. */
                                  offsets[offset]=i;
                                  /* permute remaining text. */
                                  permute(offset+1, offsets, text, text_size);
                          }
                  }
          } else {
                  /* print current permutation. */
                  for (i=0; i < text_size; i++) {
                          fputc(text[offsets[i]], stdout);
                  }
                  fputc('\n', stdout);
          }
      }
      
      int main(int argc, char* argv[])
      {
          int i, offsets[1024];
      
          /* print permutations of all arguments. */
          for (i=1; i < argc; i++) {
                  permute(0, offsets, argv[i], strlen(argv[i]));
          }
      
          return 0;
      }
      

      此代码是 C,根据要求,它非常快并且可以执行您想要的操作。当然它包含可能的缓冲区溢出,因为偏移缓冲区具有固定大小,但这只是一个示例,对吧?

      编辑:有人试过吗?有没有更简单或更快的解决方案?令人失望的是没有人进一步评论!

      【讨论】:

        【解决方案8】:
        void permute(string set, string prefix = ""){
            if(set.length() == 1){
                    cout<<"\n"<<prefix<<set;
            }
            else{
                    for(int i=0; i<set.length(); i++){
                            string new_prefix = prefix;
                            new_prefix.append(&set[i], 1);
                            string new_set = set;
                            new_set.erase(i, 1);
                            permute(new_set, new_prefix);
                    }
            }
        }
        

        并简单地将其用作 permute("word");

        【讨论】:

          【解决方案9】:

          不要在string的不同位置置换相同的字符。

          在 Python 中:

          def unique_permutation(a, l, r):
              if l == r:
                  print ''.join(a)
                  return
              for i in range(l, r+1):
                  if i != l and a[i] == a[l]:
                      continue
                  a[i], a[l] = a[l], a[i]
                  unique_permutation(a, l+1, r)
                  a[i], a[l] = a[l], a[i]
          

          【讨论】:

            【解决方案10】:

            算法步骤:

            1. 将给定的字符串存储到临时字符串中,比如“temp”
            2. 从临时字符串中删除重复项
            3. 最后调用“void permute(char *a, int i, int n)”函数打印给定字符串的所有排列而不重复

            我认为,这是最好和最有效的解决方案。

            【讨论】:

            • 这会影响结果的长度,这显然是错误的。
            猜你喜欢
            • 1970-01-01
            • 2021-01-25
            • 1970-01-01
            • 2012-09-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-06-19
            • 1970-01-01
            相关资源
            最近更新 更多