Computing permutations (with duplicates) - divide and conquer →
In a previous post we saw how to write a recursive function to compute permutations of characters of a string. But we were computing redundant permutations when the input string had duplicated characters, as in the input string “AAB”. What could we do to avoid the redundant computations?
Intuitively we need to avoid some recursive calls that we know will lead to redundant computations. The sub-trees corresponding to this redundant computation are marked red in the sketch below:

What it indicates is that when we have the substring that we are processing (by walking through the characters in the substring) we look at the characters we process and decide if we want to go down that recursive path.
Let’s go through a couple of paths in the recursion tree above and try to figure this out:
-
(start:0, i:0): The string we are staring with isAAB, substring starting atstartisAABand substring starting atiis alsoAAB(asiisstartto begin with). Now we swaps[start]ands[i]which are bothA. Now we call the _permute()withstartincremented by1. a.(start:1, i:1..2): Now the substring starting atstartisAB(asstartis1) and substring starting atiisAB. Furthers[start]isAands[i]is alsoA. We hence perform a swap producingAABand backtrack. Now,i:2and sos[start]isAands[i]isB. We swapAandBand getABA. -
(start:0, i:1): The string we are staring with isAAB, substring starting atstartisAABand substring starting atiis alsoAB(asiis1now). We havesubstring_from_startdifferent fromsubstring_from_ibuts[start]is the same ass[i]which means that if we do a swap, we will yield the same thing and also we would have come across this earlier as we have processed the substring corresponding to thisi. So we avoid going down this path and start working throughstart:0, i:2.
So the condition we are looking for traversing is that substring(start) and substring(i) are same and further the starting character at start and i are the same. Further, if substring(start) and substring(i) are different and the starting characters are different, we go ahead with the traversal (as in start:0, i:2).
Here is the modified permute() function with the condition incorporated:
void permute(string s, int start, int end) {
int i;
if (start == end)
cout << s << endl;
else {
for (i=start; i<=end; i++) {
// handle duplicates
if ((s.at(start) != s.at(i)) ||
((s.substr(start, end-start+1) == s.substr(start, end-i+1)) &&
(s.at(start) == s.at(i)))) {
swap(s[start], s[i]);
permute(s, start+1, end);
swap(s[start], s[i]);
}
}
}
}
Hope you can refer to the sketch above, work through the example and feel convinced.