21

Finding all the permutations of a string is by a well known Steinhaus–Johnson–Trotter algorithm. But if the string contains the repeated characters such as
AABB,
then the possible unique combinations will be 4!/(2! * 2!) = 6

One way of achieving this is that we can store it in an array or so and then remove the duplicates.

Is there any simpler way to modify the Johnson algorithm, so that we never generate the duplicated permutations. (In the most efficient way)

mok
  • 6,612
  • 3
  • 34
  • 59
titan
  • 415
  • 1
  • 3
  • 7
  • What is the definition of permutation? Is BA a valid permutation of AABB? – ElKamina Feb 09 '12 at 20:06
  • 2
    no BA is not a valid permutation of AABB. – titan Feb 09 '12 at 20:07
  • Permutation is the one sequence of shuffling the characters in the string. For a string of length n and unique characters we have a total of n! possible unique permutations – titan Feb 09 '12 at 20:08
  • You can modify Jhonson algorithm, by putting each appear of every letter in one step. – asaelr Feb 09 '12 at 20:24
  • If you can't find a way to avoid generating duplicates, you might benefit from removing duplicates as you're generating them by storing the permutations in a self-balancing BST or similar sorted structure. – Brian McFarland Feb 09 '12 at 20:39
  • To clarify (or possibly confuse the issue): if n is the number of permutations including duplicates, removing the duplicates after the fact will result in O(n log n), assuming heapsort, quicksort, etc. If you delete on insertion, you'll wind up with something closer to O(n log m) where m is the number of unique permutations (smaller than n). So as m approaches 1 (i.e. more duplicates like 'AAAA'), you'll benefit more from sorting ahead of time than you will with very long strings and few duplicates. – Brian McFarland Feb 09 '12 at 20:50

5 Answers5

5

Use the following recursive algorithm:

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;
}

As you see, it is very easy

Gangnus
  • 22,127
  • 14
  • 79
  • 132
5

First convert the string to a set of unique characters and occurrence numbers e.g. BANANA -> (3, A),(1,B),(2,N). (This could be done by sorting the string and grouping letters). Then, for each letter in the set, prepend that letter to all permutations of the set with one less of that letter (note the recursion). Continuing the "BANANA" example, we have: permutations((3,A),(1,B),(2,N)) = A:(permutations((2,A),(1,B),(2,N)) ++ B:(permutations((3,A),(2,N)) ++ N:(permutations((3,A),(1,B),(1,N))

Here is a working implementation in 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))
gcbenison
  • 10,617
  • 3
  • 39
  • 72
3

I think this problem is essentially the problem of generating multiset permutations. this paper seems to be relevant: J. F. Korsh P. S. LaFollette. Loopless array generation of multiset permutations. The Computer Journal, 47(5):612–621, 2004.

From the abstract: This paper presents a loopless algorithm to generate all permutations of a multiset. Each is obtained from its predecessor by making one transposition. It differs from previous such algorithms by using an array for the permutations but requiring storage only linear in its length.

Krystian
  • 1,368
  • 6
  • 12
1

In my solution, I generate recursively the options, try every time to add every letter that I didn't use as many times I need yet.

#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);
}

use example:

#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;
}
asaelr
  • 5,268
  • 1
  • 14
  • 22
  • Added some comments. any more suggestions? – asaelr Feb 09 '12 at 22:48
  • 2
    The most important improvement, even more so than comments, would be descriptive function/variable names. Right now you have two functions named `func` and `calc`, and variables named `arr`, `pref`, `pos`, `adr`, `p`, `l`, `i`, `z`, `p`, `s`, and `str`; none of their purposes are obvious by their names. Using more descriptive variable names will do wonders for the readability of your code. – BlueRaja - Danny Pflughoeft Feb 09 '12 at 23:09
  • 1
    Other smaller improvements: use descriptive types (`z` should be `bool`, `#include `); don't use magic numbers (the size of `arr`, the size of `p`); [don't use `strcpy()` for anything, ever](http://stackoverflow.com/questions/1258550/why-should-you-use-strncpy-instead-of-strcpy); don't forget to call `free()` on your `malloc()`'s :) – BlueRaja - Danny Pflughoeft Feb 09 '12 at 23:15
0

This is a tricky question and we need to use recursion to find all the permutations of a String, for example "AAB" permutations will be "AAB", "ABA" and "BAA". We also need to use Set to make sure there are no duplicate values.

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));
            }
        }

    }

}
mukta
  • 79
  • 1
  • 7
  • I wrote my code in java. I think the logic given in my code is pretty much understood. – mukta Jul 12 '17 at 09:37
  • You are still generating all the permutations(including the duplicates). The question mentions that the solution should avoid doing that. – AnEnigmaticBug Aug 24 '20 at 15:08