Skip to content

Instantly share code, notes, and snippets.

@Paul-Browne
Created February 2, 2025 12:27
Show Gist options
  • Save Paul-Browne/85b5521e11e168e826cd9c94e8ce1846 to your computer and use it in GitHub Desktop.
Save Paul-Browne/85b5521e11e168e826cd9c94e8ce1846 to your computer and use it in GitHub Desktop.
find all permutations of a string
// Heap's algorithm (iterative version, recursive version took waaaay longer)
// https://en.wikipedia.org/wiki/Heap%27s_algorithm
// Even though I kinda understand the algorithm,
// *after* reading about it and converting it to JS
// I can't pretend that I came up with this solution on my own.
const permutate = str => {
const lengthOfString = str.length;
// split the string into an array
const arr = str.split('');
// store the results
// initialize with the original string, save one iteration
const results = [str];
// store the "count" of each element
// initialize with zero's eg. [0, 0, 0, 0, 0]
// this is used to track the "stack state" since we need to simulate the recursion
const count = Array(lengthOfString).fill(0);
let i = 1;
while (i < lengthOfString) {
if (count[i] < i) {
// if i is odd, then j = count[i], else j = 0
let j = i % 2 ? count[i] : 0;
// swap the elements
[arr[j], arr[i]] = [arr[i], arr[j]];
// increment of the while-loop counter
count[i] += 1;
// simulate recursion hitting the base case
i = 1;
// push the result
results.push(arr.join(''));
} else {
// reset the state and pop the stack by incrementing i
count[i] = 0;
i += 1;
}
}
// remove duplicates
return [...new Set(results)];
}
const STRING = 'abcccdef';
console.time('permutate');
const answers = permutate(STRING);
console.timeEnd('permutate');
console.log(answers);
// FOR TESTING
// if the length of the array of permutations is equal
// to the number of possible permutations, the test passes
// Math.factorial(n) is not available in javascript
// so I used a recursive factorial function
const factorial = num => num ? factorial(num - 1) * num : 1
const frequencyOfLetters = str => str.split("").reduce((acc, cur) => (acc[cur] = acc[cur] + 1 || 1, acc), {});
const possiblePermutations = str => {
const frequencyTable = frequencyOfLetters(str);
const factorialOfLengthOfWord = factorial(str.length);
const sumFactorialOfFrequencies = Object.values(frequencyTable).reduce((acc, cur) => acc * factorial(cur), 1);
return factorialOfLengthOfWord / sumFactorialOfFrequencies;
}
const testLengthOfAnswer = (str, answersArray) => {
if (possiblePermutations(str) === answersArray.length) {
console.log('Test passed');
} else {
console.log('Test failed');
}
}
testLengthOfAnswer(STRING, answers);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment