查找字符串的所有唯一排列而不生成重复项

时间:2012-02-09 20:00:35

标签: c algorithm combinatorics

通过众所周知的Steinhaus-Johnson-Trotter算法找到字符串的所有排列。但是如果字符串包含重复的字符,例如
AABB,
然后可能的唯一组合将是4!/(2!* 2!)= 6

实现这一目标的一种方法是我们可以将它存储在数组中,然后删除重复数据。

是否有任何更简单的方法来修改Johnson算法,因此我们永远不会生成重复的排列。 (以最有效的方式)

5 个答案:

答案 0 :(得分:5)

使用以下递归算法:

PermutList Permute(SymArray fullSymArray){
    PermutList resultList=empty;
    for( each symbol A in fullSymArray, but repeated ones take only once) {
       PermutList lesserPermutList=  Permute(fullSymArray without A)
       for ( each SymArray item in lesserPermutList){
            resultList.add("A"+item);
       }
    }
    return resultList;
}

如你所见,这很容易

答案 1 :(得分:3)

我认为这个问题本质上是生成 multiset排列的问题。这篇论文似乎是相关的:J。F. Korsh P. S. LaFollette。无环阵列生成 multiset排列。 The Computer Journal,47(5):612-621,2004。

摘自:本文提出了一种无循环算法来生成多集的所有排列。每个都是通过进行一次换位从其前身获得的。它与以前的此类算法不同,它使用数组进行排列,但要求存储的长度只是线性的。

答案 2 :(得分:3)

首先将字符串转换为一组唯一字符和出现次数,例如香蕉 - > (3,A),(1,B),(2,N)。 (这可以通过排序字符串和分组字母来完成)。然后,对于集合中的每个字母,将该字母前置到集合的所有排列中,少一个字母(注意递归)。继续“BANANA”的例子,我们有:排列((3,A),(1,B),(2,N))= A :(排列((2,A),(1,B),(2) ,N))++ B :(置换((3,A),(2,N))++ N :(置换((3,A),(1,B),(1,N))

以下是Haskell中的一个有效实现:

circularPermutations::[a]->[[a]]
circularPermutations xs = helper [] xs []
                          where helper acc [] _ = acc
                                helper acc (x:xs) ys =
                                  helper (((x:xs) ++ ys):acc) xs (ys ++ [x])

nrPermutations::[(Int, a)]->[[a]]
nrPermutations x | length x == 1 = [take (fst (head x)) (repeat (snd (head x)))]
nrPermutations xs = concat (map helper (circularPermutations xs))
  where helper ((1,x):xs) = map ((:) x)(nrPermutations xs)
        helper ((n,x):xs) = map ((:) x)(nrPermutations ((n - 1, x):xs))

答案 3 :(得分:1)

在我的解决方案中,我递归地生成选项,每次尝试添加我不需要使用的每个字母。

#include <string.h>

void fill(char ***adr,int *pos,char *pref) {
    int i,z=1;
    //loop on the chars, and check if should use them
    for (i=0;i<256;i++)
        if (pos[i]) {
            int l=strlen(pref);
            //add the char
            pref[l]=i;
            pos[i]--;
            //call the recursion
            fill(adr,pos,pref);
            //delete the char
            pref[l]=0;
            pos[i]++;
            z=0;
        }
    if (z) strcpy(*(*adr)++,pref);
}

void calc(char **arr,const char *str) {
    int p[256]={0};
    int l=strlen(str);
    char temp[l+1];
    for (;l>=0;l--) temp[l]=0;
    while (*str) p[*str++]++;
    fill(&arr,p,temp);
}

使用示例:

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

int main() {
    char s[]="AABAF";
    char *arr[20];
    int i;
    for (i=0;i<20;i++) arr[i]=malloc(sizeof(s));
    calc(arr,s);
    for (i=0;i<20;i++) printf("%d: %s\n",i,arr[i]);
    return 0;
}

答案 4 :(得分:1)

这是一个棘手的问题,我们需要使用递归来查找字符串的所有排列,例如“AAB”排列将是“AAB”,“ABA”和“BAA”。 我们还需要使用设置来确保没有重复值。

import java.io.*;
import java.util.HashSet;
import java.util.*;
class Permutation {

    static HashSet<String> set = new HashSet<String>();
    public static void main (String[] args) {
    Scanner in = new Scanner(System.in);
        System.out.println("Enter :");
        StringBuilder  str = new StringBuilder(in.nextLine());
        NONDuplicatePermutation("",str.toString());  //WITHOUT DUPLICATE PERMUTATION OF STRING
        System.out.println(set);
    }


    public static void NONDuplicatePermutation(String prefix,String str){
        //It is nlogn
        if(str.length()==0){
            set.add(prefix);
        }else{
            for(int i=0;i<str.length();i++){

                NONDuplicatePermutation(prefix+ str.charAt(i), str.substring(0,i)+str.substring(i+1));
            }
        }

    }

}