2015-05-03 05:06:21 +08:00
{
"cells": [
2015-06-18 04:36:55 +08:00
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
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-26 16:47:15 +08:00
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\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-26 16:47:15 +08:00
"## Constraints and Assumptions\n",
2015-05-03 05:06:21 +08:00
"\n",
2015-06-26 16:47:15 +08:00
"*Problem statements are often intentionally 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",
"* Can I assume the string is ASCII?\n",
" * 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",
"* You can short circuit if the lengths of each string are not equal, len() in Python is generally O(1)\n",
"\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",
"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"
]
},
2015-06-26 05:36:28 +08:00
{
"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.*"
]
},
2015-06-25 06:22:25 +08:00
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Success: test_permutation\n",
"Success: test_permutation\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
2015-05-03 05:06:21 +08:00
"\n",
2015-06-25 06:22:25 +08:00
"class Test(object):\n",
" 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",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_permutation(permutations)\n",
" test.test_permutation(permutations_alt)"
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
}