{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/interactive-coding-challenges)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Solution Notebook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Problem: Find all permutations of an input string.\n", "\n", "* [Constraints](#Constraints)\n", "* [Test Cases](#Test-Cases)\n", "* [Algorithm](#Algorithm)\n", "* [Code](#Code)\n", "* [Unit Test](#Unit-Test)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Constraints\n", "\n", "* Can the input have duplicates?\n", " * Yes\n", "* Can the output have duplicates?\n", " * No\n", "* Is the output a list of strings?\n", " * Yes\n", "* Do we have to output the results in sorted order?\n", " * No\n", "* Can we assume the inputs are valid?\n", " * No\n", "* Can we assume this fits memory?\n", " * Yes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Test Cases\n", "\n", "
\n", "* None -> None\n", "* '' -> ''\n", "* 'AABC' -> ['AABC', 'AACB', 'ABAC', 'ABCA',\n", " 'ACAB', 'ACBA', 'BAAC', 'BACA',\n", " 'BCAA', 'CAAB', 'CABA', 'CBAA']\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Algorithm\n", "\n", "* Build a dictionary of {chars: counts} where counts is the number of times each char is found in the input\n", "* Loop through each item in the dictionary\n", " * If the counts is 0, continue\n", " * Decrement the current char's count in the dictionary\n", " * Add the current char to the current results\n", " * If the current result is the same length as the input, add it to the results\n", " * Else, recurse\n", " * Backtrack by:\n", " * Removing the just added current char from the current results\n", " * Incrementing the current char's count in the dictionary\n", "\n", "Complexity:\n", "* Time: O(n!)\n", "* Space: O(n!) since we are storing the results in an array, or O(n) if we are just printing each result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Code" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from collections import OrderedDict\n", "\n", "\n", "class Permutations(object):\n", "\n", " def _build_counts_map(self, string):\n", " counts_map = OrderedDict()\n", " for char in string:\n", " if char in counts_map:\n", " counts_map[char] += 1\n", " else:\n", " counts_map[char] = 1\n", " return counts_map\n", "\n", " def find_permutations(self, string):\n", " if string is None or string == '':\n", " return string\n", " counts_map = self._build_counts_map(string)\n", " curr_results = []\n", " results = []\n", " self._find_permutations(counts_map, curr_results, results, len(string))\n", " return results\n", "\n", " def _find_permutations(self, counts_map, curr_result,\n", " results, input_length):\n", " for char in counts_map:\n", " if counts_map[char] == 0:\n", " continue\n", " curr_result.append(char)\n", " counts_map[char] -= 1\n", " if len(curr_result) == input_length:\n", " results.append(''.join(curr_result))\n", " else:\n", " self._find_permutations(counts_map, curr_result,\n", " results, input_length)\n", " counts_map[char] += 1\n", " curr_result.pop()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Unit Test" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting test_permutations.py\n" ] } ], "source": [ "%%writefile test_permutations.py\n", "import unittest\n", "\n", "\n", "class TestPermutations(unittest.TestCase):\n", "\n", " def test_permutations(self):\n", " permutations = Permutations()\n", " self.assertEqual(permutations.find_permutations(None), None)\n", " self.assertEqual(permutations.find_permutations(''), '')\n", " string = 'AABC'\n", " expected = [\n", " 'AABC', 'AACB', 'ABAC', 'ABCA',\n", " 'ACAB', 'ACBA', 'BAAC', 'BACA',\n", " 'BCAA', 'CAAB', 'CABA', 'CBAA'\n", " ]\n", " self.assertEqual(permutations.find_permutations(string), expected)\n", " print('Success: test_permutations')\n", "\n", "\n", "def main():\n", " test = TestPermutations()\n", " test.test_permutations()\n", "\n", "\n", "if __name__ == '__main__':\n", " main()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Success: test_permutations\n" ] } ], "source": [ "%run -i test_permutations.py" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.2" } }, "nbformat": 4, "nbformat_minor": 1 }