diff --git a/sorting_searching/anagrams/__init__.py b/sorting_searching/anagrams/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sorting_searching/anagrams/anagrams_challenge.ipynb b/sorting_searching/anagrams/anagrams_challenge.ipynb new file mode 100644 index 0000000..afdb9e7 --- /dev/null +++ b/sorting_searching/anagrams/anagrams_challenge.ipynb @@ -0,0 +1,170 @@ +{ + "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": [ + "# Challenge Notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Problem: Sort an array of strings so all anagrams are next to each other.\n", + "\n", + "* [Constraints](#Constraints)\n", + "* [Test Cases](#Test-Cases)\n", + "* [Algorithm](#Algorithm)\n", + "* [Code](#Code)\n", + "* [Unit Test](#Unit-Test)\n", + "* [Solution Notebook](#Solution-Notebook)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constraints\n", + "\n", + "* Are there any other sorting requirements other than the grouping of anagrams?\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", + "* None -> Exception\n", + "* [] -> []\n", + "* General case\n", + " * Input: ['ram', 'act', 'arm', 'bat', 'cat', 'tab']\n", + " * Result: ['arm', 'ram', 'act', 'cat', 'bat', 'tab']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Algorithm\n", + "\n", + "Refer to the [Solution Notebook](). If you are stuck and need a hint, the solution notebook's algorithm discussion might be a good place to start." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from collections import OrderedDict\n", + "\n", + "\n", + "class Anagram(object):\n", + "\n", + " def group_anagrams(self, items):\n", + " # TODO: Implement me\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unit Test" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**The following unit test is expected to fail until you solve the challenge.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# %load test_anagrams.py\n", + "from nose.tools import assert_equal, assert_raises\n", + "\n", + "\n", + "class TestAnagrams(object):\n", + "\n", + " def test_group_anagrams(self):\n", + " anagram = Anagram()\n", + " assert_raises(TypeError, anagram.group_anagrams, None)\n", + " data = ['ram', 'act', 'arm', 'bat', 'cat', 'tab']\n", + " expected = ['ram', 'arm', 'act', 'cat', 'bat', 'tab']\n", + " assert_equal(anagram.group_anagrams(data), expected)\n", + "\n", + " print('Success: test_group_anagrams')\n", + "\n", + "\n", + "def main():\n", + " test = TestAnagrams()\n", + " test.test_group_anagrams()\n", + "\n", + "\n", + "if __name__ == '__main__':\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Solution Notebook\n", + "\n", + "Review the [Solution Notebook]() for a discussion on algorithms and code solutions." + ] + } + ], + "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.5.0" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/sorting_searching/anagrams/anagrams_solution.ipynb b/sorting_searching/anagrams/anagrams_solution.ipynb new file mode 100644 index 0000000..da04897 --- /dev/null +++ b/sorting_searching/anagrams/anagrams_solution.ipynb @@ -0,0 +1,220 @@ +{ + "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: Sort an array of strings so all anagrams are next to each other.\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", + "* Are there any other sorting requirements other than the grouping of anagrams?\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", + "* None -> Exception\n", + "* [] -> []\n", + "* General case\n", + " * Input: ['ram', 'act', 'arm', 'bat', 'cat', 'tab']\n", + " * Result: ['arm', 'ram', 'act', 'cat', 'bat', 'tab']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Algorithm\n", + "\n", + "
\n",
+ "Input: ['ram', 'act', 'arm', 'bat', 'cat', 'tab']\n",
+ "\n",
+ "Sort the chars for each item:\n",
+ "\n",
+ "'ram' -> 'amr'\n",
+ "'act' -> 'act'\n",
+ "'arm' -> 'amr'\n",
+ "'abt' -> 'bat'\n",
+ "'cat' -> 'act'\n",
+ "'abt' -> 'tab'\n",
+ "\n",
+ "Use a map of sorted chars to each item to group anagrams:\n",
+ "\n",
+ "{\n",
+ " 'amr': ['ram', 'arm'], \n",
+ " 'act': ['act', 'cat'], \n",
+ " 'abt': ['bat', 'tab']\n",
+ "}\n",
+ "\n",
+ "Result: ['arm', 'ram', 'act', 'cat', 'bat', 'tab']\n",
+ "\n",
+ "\n",
+ "Complexity:\n",
+ "* Time: O(k * n), due to the modified bucket sort\n",
+ "* Space: O(n)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Code"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "from collections import OrderedDict\n",
+ "\n",
+ "\n",
+ "class Anagram(object):\n",
+ "\n",
+ " def group_anagrams(self, items):\n",
+ " if items is None:\n",
+ " raise TypeError('items cannot be None')\n",
+ " if not items:\n",
+ " return items\n",
+ " anagram_map = OrderedDict()\n",
+ " for item in items:\n",
+ " # Use a tuple, which is hashable and\n",
+ " # serves as the key in anagram_map\n",
+ " sorted_chars = tuple(sorted(item))\n",
+ " if sorted_chars in anagram_map:\n",
+ " anagram_map[sorted_chars].append(item)\n",
+ " else:\n",
+ " anagram_map[sorted_chars] = [item]\n",
+ " result = []\n",
+ " for value in anagram_map.values():\n",
+ " result.extend(value)\n",
+ " return result"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Unit Test"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Overwriting test_anagrams.py\n"
+ ]
+ }
+ ],
+ "source": [
+ "%%writefile test_anagrams.py\n",
+ "from nose.tools import assert_equal, assert_raises\n",
+ "\n",
+ "\n",
+ "class TestAnagrams(object):\n",
+ "\n",
+ " def test_group_anagrams(self):\n",
+ " anagram = Anagram()\n",
+ " assert_raises(TypeError, anagram.group_anagrams, None)\n",
+ " data = ['ram', 'act', 'arm', 'bat', 'cat', 'tab']\n",
+ " expected = ['ram', 'arm', 'act', 'cat', 'bat', 'tab']\n",
+ " assert_equal(anagram.group_anagrams(data), expected)\n",
+ "\n",
+ " print('Success: test_group_anagrams')\n",
+ "\n",
+ "\n",
+ "def main():\n",
+ " test = TestAnagrams()\n",
+ " test.test_group_anagrams()\n",
+ "\n",
+ "\n",
+ "if __name__ == '__main__':\n",
+ " main()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Success: test_group_anagrams\n"
+ ]
+ }
+ ],
+ "source": [
+ "%run -i test_anagrams.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.5.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/sorting_searching/anagrams/test_anagrams.py b/sorting_searching/anagrams/test_anagrams.py
new file mode 100644
index 0000000..6395b0d
--- /dev/null
+++ b/sorting_searching/anagrams/test_anagrams.py
@@ -0,0 +1,22 @@
+from nose.tools import assert_equal, assert_raises
+
+
+class TestAnagrams(object):
+
+ def test_group_anagrams(self):
+ anagram = Anagram()
+ assert_raises(TypeError, anagram.group_anagrams, None)
+ data = ['ram', 'act', 'arm', 'bat', 'cat', 'tab']
+ expected = ['ram', 'arm', 'act', 'cat', 'bat', 'tab']
+ assert_equal(anagram.group_anagrams(data), expected)
+
+ print('Success: test_group_anagrams')
+
+
+def main():
+ test = TestAnagrams()
+ test.test_group_anagrams()
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file