2015-05-03 05:06:21 +08:00
{
"cells": [
2015-06-18 04:36:55 +08:00
{
"cell_type": "markdown",
"metadata": {},
"source": [
2015-07-04 07:56:38 +08:00
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://github.com/donnemartin/coding-challenges).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Solution Notebook"
2015-06-18 04:36:55 +08:00
]
},
2015-05-03 05:06:21 +08:00
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Determine if a string is a permutation of another string\n",
"\n",
2015-06-30 17:55:58 +08:00
"* [Constraints](#Constraints)\n",
2015-05-03 05:06:21 +08:00
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm: Compare Sorted Strings](#Algorithm:-Compare-Sorted-Strings)\n",
"* [Code: Compare Sorted Strings](#Code:-Compare-Sorted-Strings)\n",
2015-05-03 05:10:59 +08:00
"* [Algorithm: Hashmap Lookup](#Algorithm:-Hash-Map-Lookup)\n",
2015-06-25 06:22:25 +08:00
"* [Code: Hashmap Lookup](#Code:-Hash-Map-Lookup)\n",
"* [Unit Test](#Unit-Test)"
2015-05-03 05:06:21 +08:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
2015-06-28 19:13:27 +08:00
"## Constraints\n",
2015-05-03 05:06:21 +08:00
"\n",
2015-07-04 07:56:38 +08:00
"*Problem statements are sometimes ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
2015-06-25 06:22:25 +08:00
"\n",
2015-07-04 07:56:38 +08:00
"* Can we assume the string is ASCII?\n",
2015-06-25 06:22:25 +08:00
" * Yes\n",
" * Note: Unicode strings could require special handling depending on your language\n",
2015-05-03 05:06:21 +08:00
"* Is whitespace important?\n",
" * Yes\n",
"* Is this case sensitive? 'Nib', 'bin' is not a match?\n",
" * Yes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test Cases\n",
"\n",
"* One or more empty strings -> False\n",
"* 'Nib', 'bin' -> False\n",
"* 'act', 'cat' -> True\n",
"* 'a ct', 'ca t' -> True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm: Compare Sorted Strings\n",
"\n",
"Anagrams contain the same strings but in different orders. This approach could be slow for large strings due to sorting.\n",
"\n",
"* Sort both strings\n",
"* If both sorted strings are equal\n",
" * return True\n",
"* Else\n",
" * return False\n",
"\n",
"Complexity:\n",
"* Time: O(n log n) from the sort, in general\n",
"* Space: Additional O(l + m) is created by the sorting algorithm, where l is the length of one string and m is the length of the other"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code: Compare Sorted Strings"
]
},
{
"cell_type": "code",
2015-06-25 06:22:25 +08:00
"execution_count": 1,
2015-05-03 05:06:21 +08:00
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def permutations(str1, str2):\n",
2015-06-25 06:22:25 +08:00
" return sorted(str1) == sorted(str2)"
2015-05-03 05:06:21 +08:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm: Hash Map Lookup\n",
"\n",
"We'll keep a hash map (dict) to keep track of characters we encounter. \n",
"\n",
"Steps:\n",
"* Scan each character\n",
"* For each character in each string:\n",
" * If the character does not exist in a hash map, add the character to a hash map\n",
" * Else, increment the character's count\n",
"* If the hash maps for each string are equal\n",
" * Return True\n",
"* Else\n",
" * Return False\n",
"\n",
"Notes:\n",
2015-06-25 06:22:25 +08:00
"* Since the characters are in ASCII, we could potentially use an array of size 128 (or 256 for extended ASCII), where each array index is equivalent to an ASCII value\n",
2015-05-03 05:06:21 +08:00
"* Instead of using two hash maps, you could use one hash map and increment character values based on the first string and decrement based on the second string\n",
2015-07-04 07:56:38 +08:00
"* You can short circuit if the lengths of each string are not equal, although len() in Python is generally O(1) unlike other languages like C where getting the length of a string is O(n)\n",
2015-05-03 05:06:21 +08:00
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: Additional O(m), where m is the number of unique characters in the hash map"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
2015-05-03 05:10:59 +08:00
"## Code: Hash Map Lookup"
2015-05-03 05:06:21 +08:00
]
},
{
"cell_type": "code",
2015-06-25 06:22:25 +08:00
"execution_count": 2,
2015-05-03 05:06:21 +08:00
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from collections import defaultdict\n",
"\n",
2015-07-04 07:56:38 +08:00
"\n",
2015-05-03 05:06:21 +08:00
"def unique_counts(string):\n",
" dict_chars = defaultdict(int)\n",
" for char in string:\n",
" dict_chars[char] += 1\n",
" return dict_chars\n",
"\n",
2015-06-22 09:45:32 +08:00
"def permutations_alt(str1, str2):\n",
2015-05-03 05:06:21 +08:00
" if len(str1) != len(str2):\n",
" return False\n",
" unique_counts1 = unique_counts(str1)\n",
" unique_counts2 = unique_counts(str2)\n",
2015-06-25 06:22:25 +08:00
" return unique_counts1 == unique_counts2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Unit Test"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
2015-07-04 07:56:38 +08:00
"Overwriting test_permutation_solution.py\n"
2015-06-25 06:22:25 +08:00
]
}
],
"source": [
2015-07-04 07:56:38 +08:00
"%%writefile test_permutation_solution.py\n",
2015-06-25 06:22:25 +08:00
"from nose.tools import assert_equal\n",
2015-05-03 05:06:21 +08:00
"\n",
2015-07-04 07:56:38 +08:00
"\n",
"class TestPermutation(object):\n",
" \n",
2015-06-25 06:22:25 +08:00
" def test_permutation(self, func):\n",
" assert_equal(func('', 'foo'), False)\n",
" assert_equal(func('Nib', 'bin'), False)\n",
" assert_equal(func('act', 'cat'), True)\n",
" assert_equal(func('a ct', 'ca t'), True)\n",
" print('Success: test_permutation')\n",
"\n",
2015-07-04 07:56:38 +08:00
"def main():\n",
" test = TestPermutation()\n",
2015-06-25 06:22:25 +08:00
" test.test_permutation(permutations)\n",
2015-07-04 07:56:38 +08:00
" try:\n",
" test.test_permutation(permutations_alt)\n",
" except NameError:\n",
" # Alternate solutions are only defined\n",
" # in the solutions file\n",
" pass\n",
" \n",
"if __name__ == '__main__':\n",
" main()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Success: test_permutation\n",
"Success: test_permutation\n"
]
}
],
"source": [
"run -i test_permutation_solution.py"
2015-05-03 05:06:21 +08:00
]
}
],
"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",
2015-06-18 04:36:55 +08:00
"version": "2.7.10"
2015-05-03 05:06:21 +08:00
}
},
"nbformat": 4,
"nbformat_minor": 0
}