I\'m working on a Free Code Camp problem - http://www.freecodecamp.com/challenges/bonfire-no-repeats-please
The problem description is as follows -
<
It seemed like a straightforward enough problem, but I spent hours on the wrong track before finally figuring out the correct logic. To find all permutations of a string with one or multiple repeated characters, while keeping identical characters seperated:
Start with a string like:
abcdabc
Seperate the first occurances from the repeats:
firsts: abcd
repeats: abc
Find all permutations of the firsts:
abcd abdc adbc adcb ...
Then, one by one, insert the repeats into each permutation, following these rules:
- Start with the repeated character whose original comes first in the firsts
e.g. when insertingabcintodbac, usebfirst- Put the repeat two places or more after the first occurance
e.g. when insertingbintodbac, results aredbabcanddbacb- Then recurse for each result with the remaining repeated characters
I've seen this question with one repeated character, where the number of permutations of abcdefa where the two a's are kept seperate is given as 3600. However, this way of counting considers abcdefa and abcdefa to be two distinct permutations, "because the a's are swapped". In my opinion, this is just one permutation and its double, and the correct answer is 1800; the algorithm below will return both results.
function seperatedPermutations(str) {
var total = 0, firsts = "", repeats = "";
for (var i = 0; i < str.length; i++) {
char = str.charAt(i);
if (str.indexOf(char) == i) firsts += char; else repeats += char;
}
var firsts = stringPermutator(firsts);
for (var i = 0; i < firsts.length; i++) {
insertRepeats(firsts[i], repeats);
}
alert("Permutations of \"" + str + "\"\ntotal: " + (Math.pow(2, repeats.length) * total) + ", unique: " + total);
// RECURSIVE CHARACTER INSERTER
function insertRepeats(firsts, repeats) {
var pos = -1;
for (var i = 0; i < firsts.length, pos < 0; i++) {
pos = repeats.indexOf(firsts.charAt(i));
}
var char = repeats.charAt(pos);
for (var i = firsts.indexOf(char) + 2; i <= firsts.length; i++) {
var combi = firsts.slice(0, i) + char + firsts.slice(i);
if (repeats.length > 1) {
insertRepeats(combi, repeats.slice(0, pos) + repeats.slice(pos + 1));
} else {
document.write(combi + "
");
++total;
}
}
}
// STRING PERMUTATOR (after Filip Nguyen)
function stringPermutator(str) {
var fact = [1], permutations = [];
for (var i = 1; i <= str.length; i++) fact[i] = i * fact[i - 1];
for (var i = 0; i < fact[str.length]; i++) {
var perm = "", temp = str, code = i;
for (var pos = str.length; pos > 0; pos--) {
var sel = code / fact[pos - 1];
perm += temp.charAt(sel);
code = code % fact[pos - 1];
temp = temp.substring(0, sel) + temp.substring(sel + 1);
}
permutations.push(perm);
}
return permutations;
}
}
seperatedPermutations("abfdefa");
A calculation based on this logic of the number of results for a string like abfdefa, with 5 "first" characters and 2 repeated characters (A and F) , would be:
- The 5 "first" characters create 5! = 120 permutations
- Each character can be in 5 positions, with 24 permutations each:
A****(24)
*A***(24)
**A**(24)
***A*(24)
****A(24)- For each of these positions, the repeat character has to come at least 2 places after its "first", so that makes 4, 3, 2 and 1 places respectively (for the last position, a repeat is impossible). With the repeated character inserted, this makes 240 permutations:
A*****(24 * 4)
*A****(24 * 3)
**A***(24 * 2)
***A**(24 * 1)- In each of these cases, the second character that will be repeated could be in 6 places, and the repeat character would have 5, 4, 3, 2, and 1 place to go. However, the second (F) character cannot be in the same place as the first (A) character, so one of the combinations is always impossible:
A******(24 * 4 * (0+4+3+2+1)) = 24 * 4 * 10 = 960
*A*****(24 * 3 * (5+0+3+2+1)) = 24 * 3 * 11 = 792
**A****(24 * 2 * (5+4+0+2+1)) = 24 * 2 * 12 = 576
***A***(24 * 1 * (5+4+3+0+1)) = 24 * 1 * 13 = 312- And 960 + 792 + 576 + 312 = 2640, the expected result.
Or, for any string like abfdefa with 2 repeats:
where F is the number of "firsts".
To calculate the total without identical permutations (which I think makes more sense) you'd divide this number by 2^R, where R is the number or repeats.