diff --git a/arrays_strings/rotation/rotation_challenge.ipynb b/arrays_strings/rotation/rotation_challenge.ipynb index adf55d3..861c5ec 100644 --- a/arrays_strings/rotation/rotation_challenge.ipynb +++ b/arrays_strings/rotation/rotation_challenge.ipynb @@ -77,9 +77,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "class Rotation(object):\n", @@ -113,9 +111,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# %load test_rotation.py\n", @@ -124,19 +120,26 @@ "\n", "class TestRotation(object):\n", "\n", - " def test_rotation(self):\n", - " rotation = Rotation()\n", - " assert_equal(rotation.is_rotation('o', 'oo'), False)\n", - " assert_equal(rotation.is_rotation(None, 'foo'), False)\n", - " assert_equal(rotation.is_rotation('', 'foo'), False)\n", - " assert_equal(rotation.is_rotation('', ''), True)\n", - " assert_equal(rotation.is_rotation('foobarbaz', 'barbazfoo'), True)\n", + " def test_rotation(self, func):\n", + " assert_equal(func('o', 'oo'), False)\n", + " assert_equal(func(None, 'foo'), False)\n", + " assert_equal(func('', 'foo'), False)\n", + " assert_equal(func('', ''), True)\n", + " assert_equal(func('foobarbaz', 'barbazfoo'), True)\n", " print('Success: test_rotation')\n", "\n", "\n", "def main():\n", " test = TestRotation()\n", - " test.test_rotation()\n", + " rotation = Rotation()\n", + " test.test_rotation(rotation.is_rotation)\n", + " try:\n", + " rotation_in_place = RotationInPlace()\n", + " test.test_rotation(rotation_in_place.is_rotation)\n", + " except NameError:\n", + " # Alternate solutions are only defined\n", + " # in the solutions file\n", + " pass\n", "\n", "\n", "if __name__ == '__main__':\n", @@ -169,9 +172,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.0" + "version": "3.5.2" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } diff --git a/arrays_strings/rotation/rotation_solution.ipynb b/arrays_strings/rotation/rotation_solution.ipynb index 4f64402..9fc9fc7 100644 --- a/arrays_strings/rotation/rotation_solution.ipynb +++ b/arrays_strings/rotation/rotation_solution.ipynb @@ -62,7 +62,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Algorithm\n", + "## Algorithm: Using the Python 'in' operation\n", "\n", "Examine the following test case:\n", "* s1 = 'barbazfoo'\n", @@ -72,6 +72,10 @@ "* s2 = 'foobarbaz'\n", "* s3 = 'barbaz*foobarbaz*foo'\n", "\n", + "Notes:\n", + "* When we concatenate the strings, we need to create a new one. This is why the extra space required is O(n).\n", + "* However, this approach is very readable and concise, and using built-in Python collections operators is generally faster than writing your own, even if the theoretical time complexity is the same. Unless memory is constrained this is the better approach.\n", + "\n", "Complexity:\n", "* Time: O(n)\n", "* Space: O(n)" @@ -81,15 +85,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Code" + "## Code: Using the Python 'in' operation" ] }, { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "class Rotation(object):\n", @@ -109,15 +111,85 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Unit Test" + "## Algorithm: Comparing In Place\n", + "\n", + "Notes:\n", + "* This is taking the same high-level approach as the previous solution, but it is operating on the strings in place without creating any new string.\n", + "* This is a bit more complex and less readable.\n", + "* This approach is not using a built-in Python collection operator, so it will be slower.\n", + "\n", + "Complexity:\n", + "* Time: O(n)\n", + "* Space: O(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Code: Comparing In Place" ] }, { "cell_type": "code", "execution_count": 2, - "metadata": { - "collapsed": false - }, + "metadata": {}, + "outputs": [], + "source": [ + "class RotationInPlace(object):\n", + "\n", + " def is_substring(self, s1, s2):\n", + " # We already assured the strings are the same length; this is for readability\n", + " length = len(s1)\n", + "\n", + " # If both strings are empty, they are trivially a rotation of one another\n", + " if length == 0:\n", + " return True\n", + "\n", + " i1, i2, matching_count = 0, 0, 0\n", + " # This loop achieves the same as concatenating a string with itself\n", + " for _ in range(2*length):\n", + " \n", + " # Count consecutive character matches.\n", + " if s1[i1] == s2[i2]:\n", + " matching_count += 1\n", + " \n", + " # If the count is equal to the length of the strings, we have found a rotation\n", + " if matching_count == length:\n", + " return True\n", + " \n", + " i2 += 1\n", + " # If there is a mismatch, start the count over again\n", + " else:\n", + " matching_count, i2 = 0, 0\n", + "\n", + " # Increment i1 and wrap around if necessary\n", + " i1 += 1\n", + " if i1 == length:\n", + " i1 = 0\n", + " \n", + " # Assume there is no rotation by default\n", + " return False\n", + "\n", + " def is_rotation(self, s1, s2):\n", + " if s1 is None or s2 is None:\n", + " return False\n", + " if len(s1) != len(s2):\n", + " return False\n", + " return self.is_substring(s1, s2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unit Test" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -134,19 +206,26 @@ "\n", "class TestRotation(object):\n", "\n", - " def test_rotation(self):\n", - " rotation = Rotation()\n", - " assert_equal(rotation.is_rotation('o', 'oo'), False)\n", - " assert_equal(rotation.is_rotation(None, 'foo'), False)\n", - " assert_equal(rotation.is_rotation('', 'foo'), False)\n", - " assert_equal(rotation.is_rotation('', ''), True)\n", - " assert_equal(rotation.is_rotation('foobarbaz', 'barbazfoo'), True)\n", + " def test_rotation(self, func):\n", + " assert_equal(func('o', 'oo'), False)\n", + " assert_equal(func(None, 'foo'), False)\n", + " assert_equal(func('', 'foo'), False)\n", + " assert_equal(func('', ''), True)\n", + " assert_equal(func('foobarbaz', 'barbazfoo'), True)\n", " print('Success: test_rotation')\n", "\n", "\n", "def main():\n", " test = TestRotation()\n", - " test.test_rotation()\n", + " rotation = Rotation()\n", + " test.test_rotation(rotation.is_rotation)\n", + " try:\n", + " rotation_in_place = RotationInPlace()\n", + " test.test_rotation(rotation_in_place.is_rotation)\n", + " except NameError:\n", + " # Alternate solutions are only defined\n", + " # in the solutions file\n", + " pass\n", "\n", "\n", "if __name__ == '__main__':\n", @@ -155,15 +234,14 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": false - }, + "execution_count": 4, + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "Success: test_rotation\n", "Success: test_rotation\n" ] } @@ -175,23 +253,23 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 2", "language": "python", - "name": "python3" + "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 3 + "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.5.0" + "pygments_lexer": "ipython2", + "version": "2.7.12" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } diff --git a/arrays_strings/rotation/test_rotation.py b/arrays_strings/rotation/test_rotation.py index 89b0a20..6e9b588 100644 --- a/arrays_strings/rotation/test_rotation.py +++ b/arrays_strings/rotation/test_rotation.py @@ -3,19 +3,26 @@ from nose.tools import assert_equal class TestRotation(object): - def test_rotation(self): - rotation = Rotation() - assert_equal(rotation.is_rotation('o', 'oo'), False) - assert_equal(rotation.is_rotation(None, 'foo'), False) - assert_equal(rotation.is_rotation('', 'foo'), False) - assert_equal(rotation.is_rotation('', ''), True) - assert_equal(rotation.is_rotation('foobarbaz', 'barbazfoo'), True) + def test_rotation(self, func): + assert_equal(func('o', 'oo'), False) + assert_equal(func(None, 'foo'), False) + assert_equal(func('', 'foo'), False) + assert_equal(func('', ''), True) + assert_equal(func('foobarbaz', 'barbazfoo'), True) print('Success: test_rotation') def main(): test = TestRotation() - test.test_rotation() + rotation = Rotation() + test.test_rotation(rotation.is_rotation) + try: + rotation_in_place = RotationInPlace() + test.test_rotation(rotation_in_place.is_rotation) + except NameError: + # Alternate solutions are only defined + # in the solutions file + pass if __name__ == '__main__':