-1

I came across the question below, don't fully understand the usage of HashMap, including the lines of map.put(c, count - 1) and map.put(c, count)?

Anyone can explain?

Permutations with Duplicates: Write a method to compute all permutations of a string whose characters are not necessarily unique. The list of permutations should not have duplicates.

public static HashMap<Character, Integer> getFreqTable(String s) {
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        for (char c : s.toCharArray()) {
            if (!map.containsKey(c)) {
                map.put(c, 0);
            }
            map.put(c, map.get(c) + 1);
        }
        return map;
    }
    
    public static void getPerms(HashMap<Character, Integer> map, String prefix, int remaining, ArrayList<String> result) {
        if (remaining == 0) {
            result.add(prefix);
            return;
        }
        
        for (Character c : map.keySet()) {
            int count = map.get(c);
            if (count > 0) {
                map.put(c,  count - 1);
                printPerms(map, prefix + c, remaining - 1, result);
                map.put(c,  count);
            }
        }
    }
    
    public static ArrayList<String> getPerms(String s) {
        ArrayList<String> result = new ArrayList<String>();
        HashMap<Character, Integer> map = getFreqTable(s);
        getPerms(map, "", s.length(), result);
        return result;
    }
    
    public static void main(String[] args) {
        String s = "aab";
        ArrayList<String> result = getPerms(s);     
        System.out.println(result.toString());
    }

Update Thansk @trincot for his answer.

Sorry for not making it clear. I understand the use of HashMap, but I was looking for the reasoning for using it for this permutation question, particularly with duplicate numbers in the input.

For example, the reasoning why using HashMap and recursive backtracking can resolve this issue. I debugged and traced the getPerms but I cannot understand the backtracking logic naturally. The backtracking controls whether or not some permutation can be generated. But I cannot come up with it if I do it myself.

Below is the trace of first part of getPerms. X means if is not executed because a or b is zero.

aab -> aab,aba,baa
a2 b1  

"" 3   
  a:2
    a:1, 
     p(a,2)
        a:0
           p(aa,1)
           a: X aaa
           b: b=0
              p(aab,0)
                re: aab
              b=1
          a=1
        b:1
         b=0
          p(ab,1)
            a:0
              a=0
               p(aba,0)
                a:1
            b:0
             X abb
      a=2
   b:1

Update 2

below is another example that explains why using HashMap helps

without HashMap

   ab
    [aa, ab, ba, bb]
    
    ab
     a
      a b
       aa  
       bb
     b 
      b a 
       ba
       bb

with HashMap

ab   
[ab, ba] 

This tells using HashMap and backtracking avoid duplicate in the input

Pingpong
  • 7,681
  • 21
  • 83
  • 209

1 Answers1

0

The getFreqTable will create a HashMap that has as keys the characters of the input, and as values the count of occurrence of the corresponding character. So for input "aacbac", this function returns a HashMap that can be described as follows:

"a": 3 
"b": 1
"c": 2 

This is a very common use of a HashMap. As it provides quick lookup of a key (of a character in this case) and quick insertion of a new key, it is the ideal solution for counting the occurrence of each character in the input.

Then this map is used to select characters for a permutation. Whenever a character is selected for use in a permutation, its counter is decreased:

map.put(c,  count - 1);

And when backtracking from recursion (which will produce all permutations with that character c as prefix), that counter is restored again:

map.put(c,  count);

Whenever the counter for a certain character is 0, it cannot be selected anymore for a permutation. This is why there is this condition:

if (count > 0)

I hope this explains it.

Addendum

By maintaining the count of duplicate letters, this algorithm avoids to make an artificial distinction between these duplicate letters, by which the permutation "aa" would be considered the same as "aa", just because those two letters were swapped with eachother. A decrement of a counter does not mind where exactly that duplicate came from (position in the input). It just "takes one of them, no matter which".

trincot
  • 317,000
  • 35
  • 244
  • 286
  • Thanks for your answer, and sorry for not making it clear. I understand the use of HashMap, but I was looking for the deeper reasoning of using it for this permutation question. For example, the reasoning why using HashMap and recursive backtracking can resolve this issue. I debugged and traced the `getPerms` but I cannot understand the backtracking logic naturally. The backtracking controls whether or not some permutation can be generated. But I cannot come up with it if I do it myself. – Pingpong Sep 29 '21 at 11:21
  • Oh, then I don't really understand your question. I thought I explained "the reasoning why using HashMap and recursive backtracking" can resolve the issue... – trincot Sep 29 '21 at 11:28
  • You write *"backtracking controls whether or not some permutation can be generated"*, but there is no such logic during backtracking. There is however a 0-count check before starting the recursive process. – trincot Sep 29 '21 at 11:35
  • I updated the OP, one point is how it is used for duplicate input. Sorry, hope it is a bit more clear. – Pingpong Sep 29 '21 at 11:38
  • I think I explained the duplicate aspect: a HashMap stores *unique* keys, as it is not possible to have duplicates in such a map. Instead, a counter (which is the value for a key in the map) registers how many occurrences appeared. The counter is decremented when the character is used in a permutation, and all these counters must go to 0, but not lower. When all counters are zero (i.e. when also `remaining` is zero), you know that all characters (including duplicates) are now in the permutation. – trincot Sep 29 '21 at 11:40
  • thanks. I understand that. I think i am still missing information on why using this can solve the problem. I need to think it again and might come up with another way of expressing what I want. – Pingpong Sep 29 '21 at 11:44
  • I updated my OP with Update 2 for what I am looking for. Please check if you agree, I will mark your answer anyway. – Pingpong Sep 29 '21 at 12:09
  • You shouldn't put an answer to your question in the question. Roll back that change. Instead you may consider posting an answer in the answer section. But the hashmap's purpose is not to *avoid* duplicates in the input, but to correctly work with them, given that the permutations in the output must also have the same duplicates, and yet there shouldn't be permuations where just a pair of the *same* letters was swapped, as that doesn't produce a new permutation. – trincot Sep 29 '21 at 12:28