This notebook was prepared by Donne Martin. Source and license info is on GitHub.

Solution Notebook

Problem: Find all permutations of an input string.


  • Can the input have duplicates?
    • Yes
  • Can the output have duplicates?
    • No
  • Is the output a list of strings?
    • Yes
  • Do we have to output the results in sorted order?
    • No
  • Can we assume the inputs are valid?
    • No
  • Can we assume this fits memory?
    • Yes

Test Cases

* None -> None
* '' -> ''
* 'AABC' -> ['AABC', 'AACB', 'ABAC', 'ABCA',
             'ACAB', 'ACBA', 'BAAC', 'BACA',
             'BCAA', 'CAAB', 'CABA', 'CBAA']


  • Build a dictionary of {chars: counts} where counts is the number of times each char is found in the input
  • Loop through each item in the dictionary
    • If the counts is 0, continue
    • Decrement the current char's count in the dictionary
    • Add the current char to the current results
    • If the current result is the same length as the input, add it to the results
    • Else, recurse
    • Backtrack by:
      • Removing the just added current char from the current results
      • Incrementing the current char's count in the dictionary


  • Time: O(n!)
  • Space: O(n!) since we are storing the results in an array, or O(n) if we are just printing each result


In [1]:
from collections import OrderedDict

class Permutations(object):

    def _build_counts_map(self, string):
        counts_map = OrderedDict()
        for char in string:
            if char in counts_map:
                counts_map[char] += 1
                counts_map[char] = 1
        return counts_map

    def find_permutations(self, string):
        if string is None or string == '':
            return string
        counts_map = self._build_counts_map(string)
        curr_results = []
        results = []
        self._find_permutations(counts_map, curr_results, results, len(string))
        return results

    def _find_permutations(self, counts_map, curr_result,
                           results, input_length):
        for char in counts_map:
            if counts_map[char] == 0:
            counts_map[char] -= 1
            if len(curr_result) == input_length:
                self._find_permutations(counts_map, curr_result,
                                        results, input_length)
            counts_map[char] += 1

Unit Test

In [2]:
import unittest

class TestPermutations(unittest.TestCase):

    def test_permutations(self):
        permutations = Permutations()
        self.assertEqual(permutations.find_permutations(None), None)
        self.assertEqual(permutations.find_permutations(''), '')
        string = 'AABC'
        expected = [
            'AABC', 'AACB', 'ABAC', 'ABCA',
            'ACAB', 'ACBA', 'BAAC', 'BACA',
            'BCAA', 'CAAB', 'CABA', 'CBAA'
        self.assertEqual(permutations.find_permutations(string), expected)
        print('Success: test_permutations')

def main():
    test = TestPermutations()

if __name__ == '__main__':
In [3]:
%run -i
Success: test_permutations