字符串排列:如何去除重复排列

Permutation of String letters: How to remove repeated permutations?

本文关键字:排列 字符串 何去      更新时间:2023-10-16

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

void permute(char *a, int i, int n)
{
   int j;
   if (i == n)
     printf("%sn", 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个重复条目。

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

--

感谢

Alok Kr.

记下之前交换过的字符:

 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

标准库有您需要的:

#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

另一种方法可能是:

  1. 对阵列进行预排序。

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

  3. 所以,我们只需要看到我们修复(并排列其他(的前一个元素

  4. 若当前元素和前一个元素相同,则不要置换。

我会按照以下方式进行:首先,我生成字符的"组"(即AABBBC产生两个组:(AA) and (BBB) and (C)

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

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

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

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

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

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

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

我希望这能有所帮助。

@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,按照要求,它非常快,可以做你想做的事。当然,它包含了一个可能的缓冲区溢出,因为偏移缓冲区有一个固定的大小,但这只是一个例子,对吧?

编辑:有人试过这个吗?有没有更简单或更快的解决方案?令人失望的是再也没有人评论了!

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("单词"(;

不要在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]

算法步骤:

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

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