mirror of
https://github.com/donnemartin/interactive-coding-challenges
synced 2026-01-07 18:08:01 +00:00
Renamed top-level folders to use underscores instead of dashes.
This commit is contained in:
0
arrays_strings/__init__.py
Normal file
0
arrays_strings/__init__.py
Normal file
0
arrays_strings/compress/__init__.py
Normal file
0
arrays_strings/compress/__init__.py
Normal file
269
arrays_strings/compress/compress.ipynb
Normal file
269
arrays_strings/compress/compress.ipynb
Normal file
@@ -0,0 +1,269 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Problem: Compress a string such that 'AAABCCDDDD' becomes 'A3B1C2D4'\n",
|
||||
"\n",
|
||||
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
|
||||
"* [Test Cases](#Test-Cases)\n",
|
||||
"* [Algorithm: List](#Algorithm:-List)\n",
|
||||
"* [Code: List](#Code:-List)\n",
|
||||
"* [Algorithm: Byte Array](#Algorithm:-Byte-Array)\n",
|
||||
"* [Code: Byte array](#Code:-Byte-Array)\n",
|
||||
"* [Unit Test](#Unit-Test)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and Assumptions\n",
|
||||
"\n",
|
||||
"*Problem statements are often intentionally 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 you use additional data structures? \n",
|
||||
" * Yes\n",
|
||||
"* Is this case sensitive?\n",
|
||||
" * Yes\n",
|
||||
"* Do you compress even if it doesn't save space?\n",
|
||||
" * No"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Test Cases\n",
|
||||
"\n",
|
||||
"* NULL\n",
|
||||
"* '' -> ''\n",
|
||||
"* 'ABC' -> 'ABC'\n",
|
||||
"* 'AAABCCDDDD' -> 'A3B1C2D4'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Algorithm: List\n",
|
||||
"\n",
|
||||
"Since Python strings are immutable, we'll use a list of characters instead to exercise in-place string manipulation as you would get with a C string (which is null terminated, as seen in the diagram below). Python does not use a null-terminator.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"* Calculate the size of the compressed string\n",
|
||||
"* If the compressed string size is >= string size, return string\n",
|
||||
"* Create compressed_string\n",
|
||||
" * For each char in string\n",
|
||||
" * If char is the same as last_char, increment count\n",
|
||||
" * Else\n",
|
||||
" * Append last_char to compressed_string\n",
|
||||
" * append count to compressed_string\n",
|
||||
" * count = 1\n",
|
||||
" * last_char = char\n",
|
||||
" * Append last_char to compressed_string\n",
|
||||
" * Append count to compressed_string\n",
|
||||
" * Return compressed_string\n",
|
||||
"\n",
|
||||
"Complexity:\n",
|
||||
"* Time: O(n)\n",
|
||||
"* Space: O(2m) where m is the size of the compressed list and the resulting string copied from the list"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Code: List"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def compress_string(string):\n",
|
||||
" if string is None or len(string) == 0:\n",
|
||||
" return string\n",
|
||||
" \n",
|
||||
" # Calculate the size of the compressed string\n",
|
||||
" size = 0\n",
|
||||
" last_char = string[0]\n",
|
||||
" for char in string:\n",
|
||||
" if char != last_char:\n",
|
||||
" size += 2\n",
|
||||
" last_char = char\n",
|
||||
" size += 2\n",
|
||||
" \n",
|
||||
" # If the compressed string size is greater than \n",
|
||||
" # or equal to string size, return string\n",
|
||||
" if size >= len(string):\n",
|
||||
" return string\n",
|
||||
"\n",
|
||||
" # Create compressed_string\n",
|
||||
" compressed_string = list()\n",
|
||||
" count = 0\n",
|
||||
" last_char = string[0]\n",
|
||||
" for char in string:\n",
|
||||
" if char == last_char:\n",
|
||||
" count += 1\n",
|
||||
" else:\n",
|
||||
" compressed_string.append(last_char)\n",
|
||||
" compressed_string.append(str(count))\n",
|
||||
" count = 1\n",
|
||||
" last_char = char\n",
|
||||
" compressed_string.append(last_char)\n",
|
||||
" compressed_string.append(str(count))\n",
|
||||
" return \"\".join(compressed_string)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Algorithm: Byte Array\n",
|
||||
"\n",
|
||||
"The byte array algorithm similar when using a list, except we will need to work with the bytearray's character codes instead of the characters as we did above when we implemented this solution with a list.\n",
|
||||
"\n",
|
||||
"Complexity:\n",
|
||||
"* Time: O(n)\n",
|
||||
"* Space: O(m) where m is the size of the compressed bytearray"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Code: Byte Array"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def compress_string_alt(string):\n",
|
||||
" if string is None or len(string) == 0:\n",
|
||||
" return string\n",
|
||||
" \n",
|
||||
" # Calculate the size of the compressed string\n",
|
||||
" size = 0\n",
|
||||
" last_char_code = string[0]\n",
|
||||
" for char_code in string:\n",
|
||||
" if char_code != last_char_code:\n",
|
||||
" size += 2\n",
|
||||
" last_char_code = char_code\n",
|
||||
" size += 2\n",
|
||||
" \n",
|
||||
" # If the compressed string size is greater than \n",
|
||||
" # or equal to string size, return string \n",
|
||||
" if size >= len(string):\n",
|
||||
" return string\n",
|
||||
" \n",
|
||||
" # Create compressed_string\n",
|
||||
" compressed_string = bytearray(size)\n",
|
||||
" pos = 0\n",
|
||||
" count = 0\n",
|
||||
" last_char_code = string[0]\n",
|
||||
" for char_code in string:\n",
|
||||
" if char_code == last_char_code:\n",
|
||||
" count += 1\n",
|
||||
" else:\n",
|
||||
" compressed_string[pos] = last_char_code\n",
|
||||
" compressed_string[pos+1] = ord(str(count))\n",
|
||||
" pos += 2\n",
|
||||
" count = 1\n",
|
||||
" last_char_code = char_code\n",
|
||||
" compressed_string[pos] = last_char_code\n",
|
||||
" compressed_string[pos+1] = ord(str(count))\n",
|
||||
" return compressed_string"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 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,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Success: test_compress\n",
|
||||
"Success: test_compress\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from nose.tools import assert_equal\n",
|
||||
"\n",
|
||||
"class Test(object):\n",
|
||||
" def test_compress(self, func):\n",
|
||||
" assert_equal(func(None), None)\n",
|
||||
" assert_equal(func(''), '')\n",
|
||||
" assert_equal(func('ABC'), 'ABC')\n",
|
||||
" assert_equal(func('AAABCCDDDD'), 'A3B1C2D4')\n",
|
||||
" print('Success: test_compress')\n",
|
||||
"\n",
|
||||
"if __name__ == '__main__':\n",
|
||||
" test = Test()\n",
|
||||
" test.test_compress(compress_string)\n",
|
||||
" test.test_compress(compress_string_alt)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
0
arrays_strings/hash_map/__init__.py
Normal file
0
arrays_strings/hash_map/__init__.py
Normal file
248
arrays_strings/hash_map/hash_map.ipynb
Normal file
248
arrays_strings/hash_map/hash_map.ipynb
Normal file
@@ -0,0 +1,248 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Problem: Implement a hash table with set, get, and remove methods.\n",
|
||||
"\n",
|
||||
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
|
||||
"* [Test Cases](#Test-Cases)\n",
|
||||
"* [Algorithm](#Algorithm)\n",
|
||||
"* [Code](#Code)\n",
|
||||
"* [Pythonic-Code](#Pythonic-Code)\n",
|
||||
"* [Unit Test](#Unit-Test)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and Assumptions\n",
|
||||
"\n",
|
||||
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
|
||||
"\n",
|
||||
"* For simplicity, are the keys integers only?\n",
|
||||
" * Yes\n",
|
||||
"* For collision resolution, can we use linked lists?\n",
|
||||
" * Yes\n",
|
||||
"* Do we have to worry about load factors?\n",
|
||||
" * No"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Test Cases\n",
|
||||
"\n",
|
||||
"* get on an empty hash table index\n",
|
||||
"* set on an empty hash table index\n",
|
||||
"* set on a non empty hash table index\n",
|
||||
"* set on a key that already exists\n",
|
||||
"* remove on a key with an entry\n",
|
||||
"* remove on a key without an entry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Algorithm\n",
|
||||
"\n",
|
||||
"### Hash Function\n",
|
||||
"\n",
|
||||
"* Return key % table size\n",
|
||||
"\n",
|
||||
"Complexity:\n",
|
||||
"* Time: O(1)\n",
|
||||
"* Space: O(1)\n",
|
||||
"\n",
|
||||
"### Set\n",
|
||||
"\n",
|
||||
"* Get hash index for lookup\n",
|
||||
"* If key exists, replace\n",
|
||||
"* Else, add\n",
|
||||
"\n",
|
||||
"Complexity:\n",
|
||||
"* Time: O(1) average and best, O(n) worst\n",
|
||||
"* Space: O(1) space for newly added element\n",
|
||||
"\n",
|
||||
"### Get\n",
|
||||
"\n",
|
||||
"* Get hash index for lookup\n",
|
||||
"* If key exists, return value\n",
|
||||
"* Else, return NULL\n",
|
||||
"\n",
|
||||
"Complexity:\n",
|
||||
"* Time: O(1) average and best, O(n) worst\n",
|
||||
"* Space: O(1)\n",
|
||||
"\n",
|
||||
"### Remove\n",
|
||||
"\n",
|
||||
"* Get hash index for lookup\n",
|
||||
"* If key exists, delete the item\n",
|
||||
"\n",
|
||||
"Complexity:\n",
|
||||
"* Time: O(1) average and best, O(n) worst\n",
|
||||
"* Space: O(1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class Item(object):\n",
|
||||
" def __init__(self, key, value):\n",
|
||||
" self.key = key\n",
|
||||
" self.value = value\n",
|
||||
"\n",
|
||||
"class HashTable(object):\n",
|
||||
" def __init__(self, size):\n",
|
||||
" self.size = size\n",
|
||||
" self.table = [[] for _ in xrange(self.size)]\n",
|
||||
"\n",
|
||||
" def hash_function(self, key):\n",
|
||||
" return key % self.size\n",
|
||||
"\n",
|
||||
" def set(self, key, value):\n",
|
||||
" hash_index = self.hash_function(key)\n",
|
||||
" for item in self.table[hash_index]:\n",
|
||||
" if item.key == key:\n",
|
||||
" item.value = value\n",
|
||||
" return\n",
|
||||
" self.table[hash_index].append(Item(key, value))\n",
|
||||
"\n",
|
||||
" def get(self, key):\n",
|
||||
" hash_index = self.hash_function(key)\n",
|
||||
" for item in self.table[hash_index]:\n",
|
||||
" if item.key == key:\n",
|
||||
" return item.value\n",
|
||||
" return None\n",
|
||||
"\n",
|
||||
" def remove(self, key):\n",
|
||||
" hash_index = self.hash_function(key)\n",
|
||||
" for i, item in enumerate(self.table[hash_index]):\n",
|
||||
" if item.key == key:\n",
|
||||
" del self.table[hash_index][i]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 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": 2,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Test: get on an empty hash table index\n",
|
||||
"Test: set on an empty hash table index\n",
|
||||
"Test: set on a non empty hash table index\n",
|
||||
"Test: set on a key that already exists\n",
|
||||
"Test: remove on a key that already exists\n",
|
||||
"Test: remove on a key that doesn't exist\n",
|
||||
"Success: test_end_to_end\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from nose.tools import assert_equal\n",
|
||||
"\n",
|
||||
"class Test(object):\n",
|
||||
" # TODO: It would be better if we had unit tests for each\n",
|
||||
" # method in addition to the following end-to-end test\n",
|
||||
" def test_end_to_end(self):\n",
|
||||
" hash_table = HashTable(10)\n",
|
||||
" \n",
|
||||
" print(\"Test: get on an empty hash table index\")\n",
|
||||
" assert_equal(hash_table.get(0), None)\n",
|
||||
" \n",
|
||||
" print(\"Test: set on an empty hash table index\")\n",
|
||||
" hash_table.set(0, 'foo')\n",
|
||||
" assert_equal(hash_table.get(0), 'foo')\n",
|
||||
" hash_table.set(1, 'bar')\n",
|
||||
" assert_equal(hash_table.get(1), 'bar')\n",
|
||||
" \n",
|
||||
" print(\"Test: set on a non empty hash table index\")\n",
|
||||
" hash_table.set(10, 'foo2')\n",
|
||||
" assert_equal(hash_table.get(0), 'foo')\n",
|
||||
" assert_equal(hash_table.get(10), 'foo2')\n",
|
||||
" \n",
|
||||
" print(\"Test: set on a key that already exists\")\n",
|
||||
" hash_table.set(10, 'foo3')\n",
|
||||
" assert_equal(hash_table.get(0), 'foo')\n",
|
||||
" assert_equal(hash_table.get(10), 'foo3')\n",
|
||||
" \n",
|
||||
" print(\"Test: remove on a key that already exists\")\n",
|
||||
" hash_table.remove(10)\n",
|
||||
" assert_equal(hash_table.get(0), 'foo')\n",
|
||||
" assert_equal(hash_table.get(10), None)\n",
|
||||
" \n",
|
||||
" print(\"Test: remove on a key that doesn't exist\")\n",
|
||||
" hash_table.remove(-1)\n",
|
||||
" \n",
|
||||
" print('Success: test_end_to_end')\n",
|
||||
"\n",
|
||||
"if __name__ == '__main__':\n",
|
||||
" test = Test()\n",
|
||||
" test.test_end_to_end()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
0
arrays_strings/permutation/__init__.py
Normal file
0
arrays_strings/permutation/__init__.py
Normal file
220
arrays_strings/permutation/permutation.ipynb
Normal file
220
arrays_strings/permutation/permutation.ipynb
Normal file
@@ -0,0 +1,220 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Problem: Determine if a string is a permutation of another string\n",
|
||||
"\n",
|
||||
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
|
||||
"* [Test Cases](#Test-Cases)\n",
|
||||
"* [Algorithm: Compare Sorted Strings](#Algorithm:-Compare-Sorted-Strings)\n",
|
||||
"* [Code: Compare Sorted Strings](#Code:-Compare-Sorted-Strings)\n",
|
||||
"* [Algorithm: Hashmap Lookup](#Algorithm:-Hash-Map-Lookup)\n",
|
||||
"* [Code: Hashmap Lookup](#Code:-Hash-Map-Lookup)\n",
|
||||
"* [Unit Test](#Unit-Test)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and Assumptions\n",
|
||||
"\n",
|
||||
"*Problem statements are often intentionally 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",
|
||||
"* 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",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def permutations(str1, str2):\n",
|
||||
" return sorted(str1) == sorted(str2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"* 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",
|
||||
"* 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": [
|
||||
"## Code: Hash Map Lookup"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"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",
|
||||
"def permutations_alt(str1, str2):\n",
|
||||
" if len(str1) != len(str2):\n",
|
||||
" return False\n",
|
||||
" unique_counts1 = unique_counts(str1)\n",
|
||||
" unique_counts2 = unique_counts(str2)\n",
|
||||
" return unique_counts1 == unique_counts2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 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,
|
||||
"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",
|
||||
"\n",
|
||||
"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)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
0
arrays_strings/replace_char/__init__.py
Normal file
0
arrays_strings/replace_char/__init__.py
Normal file
210
arrays_strings/replace_char/replace_char.ipynb
Normal file
210
arrays_strings/replace_char/replace_char.ipynb
Normal file
@@ -0,0 +1,210 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Problem: Given a string, replace in-place all spaces with '%20'\n",
|
||||
"\n",
|
||||
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
|
||||
"* [Test Cases](#Test-Cases)\n",
|
||||
"* [Algorithm](#Algorithm)\n",
|
||||
"* [Code](#Code)\n",
|
||||
"* [Pythonic-Code: Not In-Place](#Pythonic-Code:-Not-In-Place)\n",
|
||||
"* [Unit Test](#Unit-Test)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and Assumptions\n",
|
||||
"\n",
|
||||
"*Problem statements are often intentionally 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",
|
||||
"* Is there enough space in the data structure for this operation?\n",
|
||||
" * Yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Test Cases\n",
|
||||
"\n",
|
||||
"* NULL->NULL\n",
|
||||
"* ' ' -> '%20'\n",
|
||||
"* ' foo bar ' -> '%20foo%20bar%20'\n",
|
||||
"* 'foo' -> 'foo'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Algorithm\n",
|
||||
"\n",
|
||||
"Since Python strings are immutable, we'll use a bytearray instead to exercise in-place string manipulation as you would get with a C string (which is null terminated, as seen in the diagram below). Python does not use a null-terminator.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"* Count the number of spaces in the bytearray\n",
|
||||
"* Determine the new bytearray length\n",
|
||||
"* For each character code in the bytearray, starting from the end of the string\n",
|
||||
" * If the character code is a space\n",
|
||||
" * bytearray[new length] = '0',\n",
|
||||
" * bytearray[new length - 1] = '2',\n",
|
||||
" * bytearray[new length - 2] = '%',\n",
|
||||
" * new length -= 3\n",
|
||||
" * Else\n",
|
||||
" * bytearray[new length] = character code,\n",
|
||||
" * new length -= 1\n",
|
||||
"\n",
|
||||
"Complexity:\n",
|
||||
"* Time: O(n)\n",
|
||||
"* Space: In-place"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def encode_spaces(string, length):\n",
|
||||
" if string is None:\n",
|
||||
" return\n",
|
||||
" num_spaces = string.count(' ')\n",
|
||||
" new_length = length + 2 * num_spaces - 1\n",
|
||||
" for i in xrange(length-1, -1, -1):\n",
|
||||
" if chr(string[i]) == ' ':\n",
|
||||
" string[new_length] = '0'\n",
|
||||
" string[new_length-1] = '2'\n",
|
||||
" string[new_length-2] = '%'\n",
|
||||
" new_length -= 3\n",
|
||||
" else:\n",
|
||||
" string[new_length] = string[i]\n",
|
||||
" new_length -= 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Pythonic-Code: Not In-Place\n",
|
||||
"\n",
|
||||
"The following code is Pythonic, but requires using additional data structures as Python strings are immutable. You could use a bytearray or a list instead of a string to simulate manipulating an array of characters."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import re\n",
|
||||
"\n",
|
||||
"def encode_spaces_alt(string):\n",
|
||||
" return re.sub(' ', '%20', string)\n",
|
||||
"\n",
|
||||
"def encode_spaces_alt2(string):\n",
|
||||
" return string.replace(' ', '%20')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 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,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Success: test_replace_char\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from nose.tools import assert_equal\n",
|
||||
"\n",
|
||||
"class Test(object):\n",
|
||||
" def test_replace_char(self, func):\n",
|
||||
" str0 = None\n",
|
||||
" str1 = bytearray(' ||')\n",
|
||||
" str2 = bytearray(' foo bar ||||||')\n",
|
||||
" str3 = bytearray('foo')\n",
|
||||
" func(str0, 0)\n",
|
||||
" func(str1, 1)\n",
|
||||
" func(str2, 9)\n",
|
||||
" func(str3, 3)\n",
|
||||
" assert_equal(str0, None)\n",
|
||||
" assert_equal(str1, '%20')\n",
|
||||
" assert_equal(str2, '%20foo%20bar%20')\n",
|
||||
" assert_equal(str3, 'foo')\n",
|
||||
" print('Success: test_replace_char')\n",
|
||||
"\n",
|
||||
"if __name__ == '__main__':\n",
|
||||
" test = Test()\n",
|
||||
" test.test_replace_char(encode_spaces)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
0
arrays_strings/reverse_string/__init__.py
Normal file
0
arrays_strings/reverse_string/__init__.py
Normal file
36
arrays_strings/reverse_string/reverse_string.cpp
Normal file
36
arrays_strings/reverse_string/reverse_string.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <stdio.h>
|
||||
|
||||
void Reverse(char* str) {
|
||||
if (str) {
|
||||
char* i = str; // first letter
|
||||
char* j = str; // last letter
|
||||
|
||||
// find the end of the string
|
||||
while (*j) {
|
||||
j++;
|
||||
}
|
||||
|
||||
// don't point to the null terminator
|
||||
j--;
|
||||
|
||||
char tmp;
|
||||
|
||||
// swap chars to reverse the string
|
||||
while (i < j) {
|
||||
tmp = *i;
|
||||
*i++ = *j;
|
||||
*j-- = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
char test0[] = "";
|
||||
char test1[] = "foo";
|
||||
Reverse(NULL);
|
||||
Reverse(test0);
|
||||
Reverse(test1);
|
||||
printf("%s \n", test0);
|
||||
printf("%s \n", test1);
|
||||
return 0;
|
||||
}
|
||||
274
arrays_strings/reverse_string/reverse_string.ipynb
Normal file
274
arrays_strings/reverse_string/reverse_string.ipynb
Normal file
@@ -0,0 +1,274 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Problem: Implement a function to reverse a string.\n",
|
||||
"\n",
|
||||
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
|
||||
"* [Test Cases](#Test-Cases)\n",
|
||||
"* [Algorithm](#Algorithm)\n",
|
||||
"* [Code](#Code)\n",
|
||||
"* [Pythonic-Code](#Pythonic-Code)\n",
|
||||
"* [Unit Test](#Unit-Test)\n",
|
||||
"* [C Algorithm](C-Algorithm)\n",
|
||||
"* [C Code]()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and Assumptions\n",
|
||||
"\n",
|
||||
"*Problem statements are often intentionally 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",
|
||||
" * Yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Test Cases\n",
|
||||
"\n",
|
||||
"* NULL input\n",
|
||||
"* '' -> ''\n",
|
||||
"* 'foo bar' -> 'rab oof'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Algorithm\n",
|
||||
"\n",
|
||||
"* Convert the string to a list\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",
|
||||
"\n",
|
||||
"Complexity:\n",
|
||||
"* Time: O(n)\n",
|
||||
"* Space: O(n), additional space converting to/from a list"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def reverse_string(string):\n",
|
||||
" if string is None:\n",
|
||||
" return None\n",
|
||||
" string_list = list(string)\n",
|
||||
" string_length = len(string_list)\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)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"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."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def reverse_string_alt(string):\n",
|
||||
" if string is None:\n",
|
||||
" return None\n",
|
||||
" return string[::-1]\n",
|
||||
"\n",
|
||||
"def reverse_string_alt2(string):\n",
|
||||
" if string is None:\n",
|
||||
" return None \n",
|
||||
" return ''.join(reversed(string))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 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,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Success: test_reversed\n",
|
||||
"Success: test_reversed\n",
|
||||
"Success: test_reversed\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"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",
|
||||
"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)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## C Algorithm\n",
|
||||
"\n",
|
||||
"This is a classic problem in C/C++\n",
|
||||
"\n",
|
||||
"We'll want to keep two pointers:\n",
|
||||
"* i is a pointer to the first char\n",
|
||||
"* j is a pointer to the last char\n",
|
||||
"\n",
|
||||
"To get a pointer to the last char, we need to loop through all characters, take note of null terminator.\n",
|
||||
"\n",
|
||||
"* while i < j\n",
|
||||
" * swap i and j\n",
|
||||
"\n",
|
||||
"Complexity:\n",
|
||||
"* Time: O(n)\n",
|
||||
"* Space: In-place\n",
|
||||
"\n",
|
||||
"Note:\n",
|
||||
"* Instead of using i, you can use str instead, although this might not be as intuitive."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## C Code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# %load reverse_string.cpp\n",
|
||||
"#include <stdio.h>\n",
|
||||
"\n",
|
||||
"void Reverse(char* str) {\n",
|
||||
" if (str) {\n",
|
||||
" char* i = str;\t// first letter\n",
|
||||
" char* j = str;\t// last letter\n",
|
||||
" \n",
|
||||
" // find the end of the string\n",
|
||||
" while (*j) {\n",
|
||||
" j++;\n",
|
||||
" }\n",
|
||||
" \n",
|
||||
" // don't point to the null terminator\n",
|
||||
" j--;\n",
|
||||
" \n",
|
||||
" char tmp;\n",
|
||||
" \n",
|
||||
" // swap chars to reverse the string\n",
|
||||
" while (i < j) {\n",
|
||||
" tmp = *i;\n",
|
||||
" *i++ = *j;\n",
|
||||
" *j-- = tmp;\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"int main() {\n",
|
||||
" char test0[] = \"\";\n",
|
||||
" char test1[] = \"foo\";\n",
|
||||
" Reverse(NULL);\n",
|
||||
" Reverse(test0);\n",
|
||||
" Reverse(test1);\n",
|
||||
" printf(\"%s \\n\", test0);\n",
|
||||
" printf(\"%s \\n\", test1);\n",
|
||||
" return 0;\n",
|
||||
"}"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
0
arrays_strings/rotation/__init__.py
Normal file
0
arrays_strings/rotation/__init__.py
Normal file
167
arrays_strings/rotation/rotation.ipynb
Normal file
167
arrays_strings/rotation/rotation.ipynb
Normal file
@@ -0,0 +1,167 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Problem: Determine if a string s1 is a rotation of another string s2. Also write a function is_substring which you can only call once to determine whether a rotation occurs\n",
|
||||
"\n",
|
||||
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
|
||||
"* [Test Cases](#Test-Cases)\n",
|
||||
"* [Algorithm](#Algorithm)\n",
|
||||
"* [Code](#Code)\n",
|
||||
"* [Unit Test](#Unit-Test)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and Assumptions\n",
|
||||
"\n",
|
||||
"*Problem statements are often intentionally 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 you use additional data structures? \n",
|
||||
" * Yes\n",
|
||||
"* Is this case sensitive?\n",
|
||||
" * Yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Test Cases\n",
|
||||
"\n",
|
||||
"* Any strings that differ in size results in False\n",
|
||||
"* NULL, 'foo' -> False (any NULL results in False)\n",
|
||||
"* ' ', 'foo' -> False\n",
|
||||
"* ' ', ' ' -> True\n",
|
||||
"* 'foobarbaz', 'barbazfoo' -> True"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Algorithm\n",
|
||||
"\n",
|
||||
"Using the following test case:\n",
|
||||
"* s1 = 'barbazfoo'\n",
|
||||
"* s2 = 'foobarbaz'\n",
|
||||
"\n",
|
||||
"We see that if we can use the given is_substring method if we take compare s2 with s1 + s1:\n",
|
||||
"* s2 = 'foobarbaz'\n",
|
||||
"* s3 = 'barbaz*foobarbaz*foo'\n",
|
||||
"\n",
|
||||
"Complexity:\n",
|
||||
"* Time: O(n)\n",
|
||||
"* Space: O(2n) of additional space for s3, which reduces to O(n)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def is_substring(s1, s2):\n",
|
||||
" return s1 in s2\n",
|
||||
"\n",
|
||||
"def is_rotation(s1, s2):\n",
|
||||
" if s1 is None or s2 is None:\n",
|
||||
" return False\n",
|
||||
" if len(s1) != len(s2):\n",
|
||||
" return False\n",
|
||||
" s3 = s1 + s1\n",
|
||||
" return is_substring(s2, s3)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 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": 2,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Success: test_is_rotation\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from nose.tools import assert_equal\n",
|
||||
"\n",
|
||||
"class Test(object):\n",
|
||||
" def test_is_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_is_rotation')\n",
|
||||
"\n",
|
||||
"if __name__ == '__main__':\n",
|
||||
" test = Test()\n",
|
||||
" test.test_is_rotation(is_rotation)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
0
arrays_strings/tests/__init__.py
Normal file
0
arrays_strings/tests/__init__.py
Normal file
0
arrays_strings/unique_chars/__init__.py
Normal file
0
arrays_strings/unique_chars/__init__.py
Normal file
250
arrays_strings/unique_chars/unique_chars.ipynb
Normal file
250
arrays_strings/unique_chars/unique_chars.ipynb
Normal file
@@ -0,0 +1,250 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Problem: Implement an algorithm to determine if a string has all unique characters\n",
|
||||
"\n",
|
||||
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
|
||||
"* [Test Cases](#Test-Cases)\n",
|
||||
"* [Algorithm 1: Sets and Length Comparison](#Algorithm-1:-Sets-and-Length-Comparison)\n",
|
||||
"* [Code: Sets and Length Comparison](#Code:-Sets-and-Length-Comparison)\n",
|
||||
"* [Algorithm 2: Hash Map Lookup](#Algorithm-2:-Hash-Map-Lookup)\n",
|
||||
"* [Code: Hash Map Lookup](#Code:-Hash-Map-Lookup)\n",
|
||||
"* [Algorithm 3: In-Place](#Algorithm-3:-In-Place)\n",
|
||||
"* [Code: In-Place](#Code:-In-Place)\n",
|
||||
"* [Unit Test](#Unit-Test)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and Assumptions\n",
|
||||
"\n",
|
||||
"*Problem statements are often intentionally 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 you use additional data structures? \n",
|
||||
" * Yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Test Cases\n",
|
||||
"\n",
|
||||
"* '' -> True\n",
|
||||
"* 'foo' -> False\n",
|
||||
"* 'bar' -> True"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Algorithm 1: Sets and Length Comparison\n",
|
||||
"\n",
|
||||
"A set is an unordered collection of unique elements. \n",
|
||||
"\n",
|
||||
"* If the length of the set(string) equals the length of the string\n",
|
||||
" * Return True\n",
|
||||
"* Else\n",
|
||||
" * Return False\n",
|
||||
" \n",
|
||||
"Complexity:\n",
|
||||
"* Time: O(n)\n",
|
||||
"* Space: Additional O(m), where m is the number of unique characters in the set"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Code: Sets and Length Comparison"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def unique_chars(string):\n",
|
||||
" return len(set(string)) == len(string)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Algorithm 2: Hash Map Lookup\n",
|
||||
"\n",
|
||||
"We'll keep a hash map (set) to keep track of unique characters we encounter. \n",
|
||||
"\n",
|
||||
"Steps:\n",
|
||||
"* Scan each character\n",
|
||||
"* For each character:\n",
|
||||
" * If the character does not exist in a hash map, add the character to a hash map\n",
|
||||
" * Else, return False\n",
|
||||
"* Return True\n",
|
||||
"\n",
|
||||
"Notes:\n",
|
||||
"* We could also use a dictionary, but it seems more logical to use a set as it does not contain duplicate elements\n",
|
||||
"* Since the characters are in ASCII, we could potentially use an array of size 128 (or 256 for extended ASCII)\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": [
|
||||
"## Code: Hash Map Lookup"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def unique_chars_hash(string):\n",
|
||||
" chars_set = set()\n",
|
||||
" for char in string:\n",
|
||||
" if char in chars_set:\n",
|
||||
" return False\n",
|
||||
" else:\n",
|
||||
" chars_set.add(char)\n",
|
||||
" return True"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Algorithm 3: In-Place\n",
|
||||
"\n",
|
||||
"Assume we cannot use additional data structures, which will eliminate the fast lookup O(1) time provided by our hash map. \n",
|
||||
"* Scan each character\n",
|
||||
"* For each character:\n",
|
||||
" * Scan all [other] characters in the array\n",
|
||||
" * Exluding the current character from the scan is rather tricky in Python and results in a non-Pythonic solution\n",
|
||||
" * If there is a match, return False\n",
|
||||
"* Return True\n",
|
||||
"\n",
|
||||
"Algorithm Complexity:\n",
|
||||
"* Time: O(n^2)\n",
|
||||
"* Space: In-place"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Code: In-Place"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def unique_chars_inplace(string):\n",
|
||||
" for char in string:\n",
|
||||
" if string.count(char) > 1:\n",
|
||||
" return False\n",
|
||||
" return True"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 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": 4,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Success: test_unique_chars\n",
|
||||
"Success: test_unique_chars\n",
|
||||
"Success: test_unique_chars\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from nose.tools import assert_equal\n",
|
||||
"\n",
|
||||
"class Test(object):\n",
|
||||
" def test_unique_chars(self, func):\n",
|
||||
" assert_equal(func(''), True)\n",
|
||||
" assert_equal(func('foo'), False)\n",
|
||||
" assert_equal(func('bar'), True)\n",
|
||||
" print('Success: test_unique_chars')\n",
|
||||
"\n",
|
||||
"if __name__ == '__main__':\n",
|
||||
" test = Test()\n",
|
||||
" test.test_unique_chars(unique_chars)\n",
|
||||
" test.test_unique_chars(unique_chars_hash)\n",
|
||||
" test.test_unique_chars(unique_chars_inplace)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
Reference in New Issue
Block a user