From 73ec41e42777bee730db7194f89d92eaad8be629 Mon Sep 17 00:00:00 2001 From: Donne Martin Date: Fri, 3 Jul 2015 19:57:09 -0400 Subject: [PATCH] Added reverse string challenge. --- .../reverse_string_challenge.ipynb | 164 ++++++++++++++++++ .../reverse_string_solution.ipynb | 110 +++++++----- .../reverse_string/test_reverse_string.py | 18 ++ 3 files changed, 249 insertions(+), 43 deletions(-) create mode 100644 arrays_strings/reverse_string/reverse_string_challenge.ipynb create mode 100644 arrays_strings/reverse_string/test_reverse_string.py diff --git a/arrays_strings/reverse_string/reverse_string_challenge.ipynb b/arrays_strings/reverse_string/reverse_string_challenge.ipynb new file mode 100644 index 0000000..a002edf --- /dev/null +++ b/arrays_strings/reverse_string/reverse_string_challenge.ipynb @@ -0,0 +1,164 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://github.com/donnemartin/coding-challenges)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Challenge Notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Problem: Implement a function to reverse a string (a list of characters), in-place.\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", + "*Problem statements are sometimes ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n", + "\n", + "* Can I assume the string is ASCII?\n", + " * Yes\n", + " * Note: Unicode strings could require special handling depending on your language\n", + "* Since we need to do this in-place, it seems we cannot use the slice operator or the reversed function?\n", + " * Correct\n", + "* Since Python string are immutable, can I use a list of characters instead?\n", + " * Yes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test Cases\n", + "\n", + "* None -> None\n", + "* [''] -> ['']\n", + "* ['f', 'o', 'o', ' ', 'b', 'a', 'r'] -> ['r', 'a', 'b', ' ', 'o', 'o', 'f']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Algorithm\n", + "\n", + "Refer to the [Solution Notebook](http://nbviewer.ipython.org/github/donnemartin/coding-challenges/blob/master/arrays_strings/reverse_string/reverse_string_solution.ipynb). 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": [ + "def list_of_chars(list_chars):\n", + " # TODO: Implement me\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unit Test" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "**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_reverse_string.py\n", + "from nose.tools import assert_equal\n", + "\n", + "\n", + "class TestReverse(object):\n", + " \n", + " def test_reverse(self):\n", + " assert_equal(list_of_chars(None), None)\n", + " assert_equal(list_of_chars(['']), [''])\n", + " assert_equal(list_of_chars(['f', 'o', 'o', ' ', 'b', 'a', 'r']), \n", + " ['r', 'a', 'b', ' ', 'o', 'o', 'f'])\n", + " print('Success: test_reverse')\n", + "\n", + "def main():\n", + " test = TestReverse()\n", + " test.test_reverse()\n", + " \n", + "if __name__ == '__main__':\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Solution Notebook\n", + "\n", + "Review the [Solution Notebook](http://nbviewer.ipython.org/github/donnemartin/coding-challenges/blob/master/arrays_strings/reverse_string/reverse_string_solution.ipynb) for a discussion on algorithms and code solutions." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/arrays_strings/reverse_string/reverse_string_solution.ipynb b/arrays_strings/reverse_string/reverse_string_solution.ipynb index af70efc..14a6a90 100644 --- a/arrays_strings/reverse_string/reverse_string_solution.ipynb +++ b/arrays_strings/reverse_string/reverse_string_solution.ipynb @@ -4,14 +4,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes)." + "This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://github.com/donnemartin/coding-challenges)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Problem: Implement a function to reverse a string.\n", + "# Solution Notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Problem: Implement a function to reverse a string (a list of characters), in-place.\n", "\n", "* [Constraints](#Constraints)\n", "* [Test Cases](#Test-Cases)\n", @@ -19,8 +26,8 @@ "* [Code](#Code)\n", "* [Pythonic-Code](#Pythonic-Code)\n", "* [Unit Test](#Unit-Test)\n", - "* [C Algorithm](C-Algorithm)\n", - "* [C Code]()" + "* [Bonus C Algorithm](#Bonus-C-Algorithm)\n", + "* [Bonus C Code](#Bonus-C-Code)" ] }, { @@ -29,14 +36,14 @@ "source": [ "## Constraints\n", "\n", - "*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n", + "*Problem statements are sometimes ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n", "\n", "* Can I assume the string is ASCII?\n", " * Yes\n", " * Note: Unicode strings could require special handling depending on your language\n", - "* Can I use the slice operator or the reversed function?\n", - " * No\n", - "* Since Python string are immutable, can I use a list instead?\n", + "* Since we need to do this in-place, it seems we cannot use the slice operator or the reversed function?\n", + " * Correct\n", + "* Since Python string are immutable, can I use a list of characters instead?\n", " * Yes" ] }, @@ -46,9 +53,9 @@ "source": [ "## Test Cases\n", "\n", - "* NULL input\n", - "* '' -> ''\n", - "* 'foo bar' -> 'rab oof'" + "* None -> None\n", + "* [''] -> ['']\n", + "* ['f', 'o', 'o', ' ', 'b', 'a', 'r'] -> ['r', 'a', 'b', ' ', 'o', 'o', 'f']" ] }, { @@ -57,15 +64,18 @@ "source": [ "## Algorithm\n", "\n", - "* Convert the string to a list\n", + "Since Python strings are immutable, we'll use a list of chars instead to exercise in-place string manipulation as you would get with a C string.\n", + "\n", "* Iterate len(string)/2 times, starting with i = 0:\n", " * Swap i and len(string) - 1 - i\n", - " * Sncrement i\n", - "* Convert the list to a string and return it\n", + " * Increment i\n", "\n", "Complexity:\n", "* Time: O(n)\n", - "* Space: O(n), additional space converting to/from a list" + "* Space: O(1)\n", + "\n", + "Note:\n", + "* You could use a byte array instead of a list to do in-place string operations" ] }, { @@ -83,15 +93,14 @@ }, "outputs": [], "source": [ - "def reverse_string(string):\n", - " if string is None:\n", + "def list_of_chars(list_chars):\n", + " if list_chars is None:\n", " return None\n", - " string_list = list(string)\n", - " string_length = len(string_list)\n", + " string_length = len(list_chars)\n", " for i in xrange(string_length/2):\n", - " string_list[i], string_list[string_length-1-i] = \\\n", - " string_list[string_length-1-i], string_list[i]\n", - " return ''.join(string_list)" + " list_chars[i], list_chars[string_length-1-i] = \\\n", + " list_chars[string_length-1-i], list_chars[i]\n", + " return list_chars" ] }, { @@ -100,7 +109,7 @@ "source": [ "## Pythonic-Code\n", "\n", - "This question has an artificial constraint that prevented the use of the slice operator and the reversed method. For completeness, the solutions for these are provided below." + "This question has an artificial constraint that prevented the use of the slice operator and the reversed method. For completeness, the solutions for these are provided below. Note these solutions are not in-place." ] }, { @@ -129,13 +138,6 @@ "## Unit Test" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "*It is important to identify and run through general and edge cases from the [Test Cases](#Test-Cases) section by hand. You generally will not be asked to write a unit test like what is shown below.*" - ] - }, { "cell_type": "code", "execution_count": 3, @@ -147,27 +149,49 @@ "name": "stdout", "output_type": "stream", "text": [ - "Success: test_reversed\n", - "Success: test_reversed\n", - "Success: test_reversed\n" + "Overwriting test_reverse_string.py\n" ] } ], "source": [ + "%%writefile test_reverse_string.py\n", "from nose.tools import assert_equal\n", "\n", - "class Test(object):\n", - " def test_reversed(self, func):\n", - " assert_equal(func(None), None)\n", - " assert_equal(func(''), '')\n", - " assert_equal(func('foo bar'), 'rab oof')\n", - " print('Success: test_reversed')\n", "\n", + "class TestReverse(object):\n", + " \n", + " def test_reverse(self):\n", + " assert_equal(list_of_chars(None), None)\n", + " assert_equal(list_of_chars(['']), [''])\n", + " assert_equal(list_of_chars(['f', 'o', 'o', ' ', 'b', 'a', 'r']), \n", + " ['r', 'a', 'b', ' ', 'o', 'o', 'f'])\n", + " print('Success: test_reverse')\n", + "\n", + "def main():\n", + " test = TestReverse()\n", + " test.test_reverse()\n", + " \n", "if __name__ == '__main__':\n", - " test = Test()\n", - " test.test_reversed(reverse_string)\n", - " test.test_reversed(reverse_string_alt)\n", - " test.test_reversed(reverse_string_alt2)" + " main()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success: test_reverse\n" + ] + } + ], + "source": [ + "%run -i test_reverse_string.py" ] }, { diff --git a/arrays_strings/reverse_string/test_reverse_string.py b/arrays_strings/reverse_string/test_reverse_string.py new file mode 100644 index 0000000..7cbc2df --- /dev/null +++ b/arrays_strings/reverse_string/test_reverse_string.py @@ -0,0 +1,18 @@ +from nose.tools import assert_equal + + +class TestReverse(object): + + def test_reverse(self): + assert_equal(list_of_chars(None), None) + assert_equal(list_of_chars(['']), ['']) + assert_equal(list_of_chars(['f', 'o', 'o', ' ', 'b', 'a', 'r']), + ['r', 'a', 'b', ' ', 'o', 'o', 'f']) + print('Success: test_reverse') + +def main(): + test = TestReverse() + test.test_reverse() + +if __name__ == '__main__': + main() \ No newline at end of file