├── .gitignore ├── LICENSE ├── README.md ├── aircraftspacing ├── aircraft_spacing_bottom_up.py ├── aircraft_spacing_opt.py ├── aircraft_spacing_rec.py └── aircraft_spacing_top_down.py ├── countingderangements ├── count_derangements_bottom_up.py ├── count_derangements_opt.py ├── count_derangements_rec.py └── count_derangements_top_down.py ├── exercise ├── full_bus_tour.py └── solution │ ├── full_bus_tour_bottom_up.py │ ├── full_bus_tour_rec.py │ └── full_bus_tour_top_down.py ├── justify ├── text_justify_dyn.py ├── text_justify_opt.py └── text_justify_rec.py ├── maxsubarray ├── max_sub_array_bottom_up.py ├── max_sub_array_opt.py ├── max_sub_array_rec.py └── max_sub_array_top_down.py ├── recursion ├── file_search.py └── timer.py ├── strdistance ├── string_distance_bottom_up.py ├── string_distance_opt.py ├── string_distance_rec.py └── string_distance_top_down.py └── test └── test_full_bus_tour.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | .idea/ 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # Environments 86 | .env 87 | .venv 88 | env/ 89 | venv/ 90 | ENV/ 91 | env.bak/ 92 | venv.bak/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 James Cutajar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DynamicProgrammingInPython 2 | Dynamic programming problems and solutions in python 3 | 4 | The udemy course for this repo can be found at: 5 | 6 | https://www.udemy.com/course/dynamic-programming-python/?couponCode=KWLQJY_GIT 7 | 8 | Please do get in touch if you find any suggestions/improvements! 9 | 10 | Follow me on https://twitter.com/cutajarj 11 | 12 | And checkout my blog at: www.cutajarjames.com 13 | 14 | Here is an awesome cactus cowboy: 15 | ``` 16 | _ _ 17 | / '-' \ 18 | ; ; 19 | /'-| |-'\ 20 | | |_______K | 21 | \ '-------' / 22 | '.___.....___.' 23 | | ; : ;| 24 | _|;__;__.|_ 25 | | Y | .--. 26 | .--. \__.'^'.__/ /; \ 27 | / ;\ |_ ; _| | ' | 28 | | ; | { `"""` } |; | 29 | |' | { } | ; | 30 | | ; | { } | | 31 | |; | ;`-.__.'| |: ;| 32 | | ; \ |; ; |_____/ ; | 33 | | '.'-----' ' -_ .' / 34 | \ '. - _ ' ; ; _ - .' 35 | '. - - ; ; .------` 36 | `--------. ;| 37 | jgs |; , | 38 | | ; | 39 | |. ; | 40 | | : :| 41 | | . | 42 | |; ; | 43 | |; , | 44 | | ; | 45 | |. ; | 46 | | : :| 47 | | . | 48 | |; ; | 49 | |; , | 50 | | ; | 51 | | ; | 52 | |. ; | 53 | | : :| 54 | | . | 55 | |; ; | 56 | `"-----"` 57 | ``` -------------------------------------------------------------------------------- /aircraftspacing/aircraft_spacing_bottom_up.py: -------------------------------------------------------------------------------- 1 | class AircraftSpacingBottomUp: 2 | def __init__(self, passengers): 3 | self.passengers = passengers 4 | self.sub_solutions = [-1] * len(passengers) 5 | for i in range(len(passengers) - 1, -1, -1): 6 | choosing_first = self.passengers[i] + (self.sub_solutions[i + 2] if i + 2 < len(self.sub_solutions) else 0) 7 | not_choosing_first = self.sub_solutions[i + 1] if i + 1 < len(self.sub_solutions) else 0 8 | self.sub_solutions[i] = max(choosing_first, not_choosing_first) 9 | 10 | def max_passengers(self): 11 | return self.sub_solutions[0] 12 | 13 | 14 | # spacing = AircraftSpacingBottomUp([155, 55, 2, 96, 67, 203, 3, 66, 32, 65, 29, 8, 299, 323, 77, 3, 28, 15 | # 128, 19, 523, 372, 2, 3, 66, 124, 38, 34, 12,88, 23 ,74,65, 87, 434, 16 | # 14, 7, 49, 38, 27, 641, 61, 58, 14, 57, 71, 11, 82, 178, 93, 191, 4]) 17 | spacing = AircraftSpacingBottomUp([155, 55, 2, 96, 67, 203, 3]) 18 | print(spacing.max_passengers()) 19 | -------------------------------------------------------------------------------- /aircraftspacing/aircraft_spacing_opt.py: -------------------------------------------------------------------------------- 1 | class AircraftSpacingOpt: 2 | def __init__(self, passengers): 3 | self.passengers = passengers 4 | self.sub_solution_i, sub_solution_i_plus_1, sub_solution_i_plus_2 = 0, 0, 0 5 | for i in range(len(passengers) - 1, -1, -1): 6 | choosing_first = self.passengers[i] + sub_solution_i_plus_2 7 | not_choosing_first = sub_solution_i_plus_1 8 | self.sub_solution_i = max(choosing_first, not_choosing_first) 9 | sub_solution_i_plus_1, sub_solution_i_plus_2 = self.sub_solution_i, sub_solution_i_plus_1 10 | 11 | def max_passengers(self): 12 | return self.sub_solution_i 13 | 14 | 15 | # spacing = AircraftSpacingOpt([155, 55, 2, 96, 67, 203, 3, 66, 32, 65, 29, 8, 299, 323, 77, 3, 28, 16 | # 128, 19, 523, 372, 2, 3, 66, 124, 38, 34, 12, 88, 23, 74, 65, 87, 434, 17 | # 14, 7, 49, 38, 27, 641, 61, 58, 14, 57, 71, 11, 82, 178, 93, 191, 4]) 18 | spacing = AircraftSpacingOpt([155, 55, 2, 96, 67, 203, 3]) 19 | print(spacing.max_passengers()) 20 | -------------------------------------------------------------------------------- /aircraftspacing/aircraft_spacing_rec.py: -------------------------------------------------------------------------------- 1 | class AircraftSpacingRec: 2 | def __init__(self, passengers): 3 | self.passengers = passengers 4 | 5 | def max_passengers(self, i): 6 | if i >= len(self.passengers): 7 | return 0 8 | choosing_first = self.passengers[i] + self.max_passengers(i + 2) 9 | not_choosing_first = self.max_passengers(i + 1) 10 | return max(choosing_first, not_choosing_first) 11 | 12 | 13 | # spacing = AircraftSpacingRec([155, 55, 2, 96, 67, 203, 3, 66, 32, 65, 29, 8, 299, 323, 77, 3, 28, 14 | # 128, 19, 523, 372, 2, 3, 66, 124, 38, 34, 12,88, 23 ,74,65, 87, 434, 15 | # 14, 7, 49, 38, 27, 641, 61, 58, 14, 57, 71, 11, 82, 178, 93, 191, 4]) 16 | spacing = AircraftSpacingRec([155, 55, 2, 96, 67, 203, 3]) 17 | print(spacing.max_passengers(0)) 18 | -------------------------------------------------------------------------------- /aircraftspacing/aircraft_spacing_top_down.py: -------------------------------------------------------------------------------- 1 | class AircraftSpacingTopDown: 2 | def __init__(self, passengers): 3 | self.passengers = passengers 4 | self.sub_solutions = [-1] * len(passengers) 5 | 6 | def max_passengers(self, i): 7 | if i >= len(self.passengers): 8 | return 0 9 | if self.sub_solutions[i] != -1: 10 | return self.sub_solutions[i] 11 | choosing_first = self.passengers[i] + self.max_passengers(i + 2) 12 | not_choosing_first = self.max_passengers(i + 1) 13 | max_pass = max(choosing_first, not_choosing_first) 14 | self.sub_solutions[i] = max_pass 15 | return max_pass 16 | 17 | 18 | # spacing = AircraftSpacingTopDown([155, 55, 2, 96, 67, 203, 3, 66, 32, 65, 29, 8, 299, 323, 77, 3, 28, 19 | # 128, 19, 523, 372, 2, 3, 66, 124, 38, 34, 12,88, 23 ,74,65, 87, 434, 20 | # 14, 7, 49, 38, 27, 641, 61, 58, 14, 57, 71, 11, 82, 178, 93, 191, 4]) 21 | spacing = AircraftSpacingTopDown([155, 55, 2, 96, 67, 203, 3]) 22 | print(spacing.max_passengers(0)) 23 | -------------------------------------------------------------------------------- /countingderangements/count_derangements_bottom_up.py: -------------------------------------------------------------------------------- 1 | class CountDerangementsBottomUp: 2 | def __init__(self, set_size): 3 | self.set_size = set_size 4 | self.sub_solutions = [-1] * (set_size + 1) 5 | for n in range(1, set_size + 1): 6 | if n == 1: 7 | self.sub_solutions[n] = 0 8 | elif n == 2: 9 | self.sub_solutions[n] = 1 10 | else: 11 | self.sub_solutions[n] = (n - 1) * (self.sub_solutions[n - 1] + 12 | self.sub_solutions[n - 2]) 13 | 14 | def count_derangements(self): 15 | return self.sub_solutions[self.set_size] 16 | 17 | 18 | # for i in range(1,64): 19 | for i in range(1, 11): 20 | n = CountDerangementsBottomUp(i).count_derangements() 21 | print("Derangements in set size {} -> {}".format(i, n)) 22 | -------------------------------------------------------------------------------- /countingderangements/count_derangements_opt.py: -------------------------------------------------------------------------------- 1 | class CountDerangementsOpt: 2 | def __init__(self, set_size): 3 | self.set_size = set_size 4 | self.solution_n, solution_n_minus_1, solution_n_minus_2 = 0, 0, 0 5 | for n in range(1, set_size + 1): 6 | if n == 1: 7 | self.solution_n = 0 8 | elif n == 2: 9 | self.solution_n = 1 10 | else: 11 | self.solution_n = (n - 1) * (solution_n_minus_1 + 12 | solution_n_minus_2) 13 | solution_n_minus_2 = solution_n_minus_1 14 | solution_n_minus_1 = self.solution_n 15 | 16 | def count_derangements(self): 17 | return self.solution_n 18 | 19 | 20 | # for i in range(1,64): 21 | for i in range(1, 11): 22 | n = CountDerangementsOpt(i).count_derangements() 23 | print("Derangments in set size {} -> {}".format(i, n)) 24 | -------------------------------------------------------------------------------- /countingderangements/count_derangements_rec.py: -------------------------------------------------------------------------------- 1 | class CountDerangementsRec: 2 | def __init__(self, set_size): 3 | self.set_size = set_size 4 | 5 | def count_derangements(self): 6 | return self.count_derangements_rec(self.set_size) 7 | 8 | def count_derangements_rec(self, n): 9 | if n == 1: 10 | return 0 11 | if n == 2: 12 | return 1 13 | return (n - 1) * (self.count_derangements_rec(n - 1) + self.count_derangements_rec(n - 2)) 14 | 15 | 16 | for i in range(1, 64): 17 | n = CountDerangementsRec(i).count_derangements() 18 | print("Derangements in set size {} -> {}".format(i, n)) 19 | 20 | -------------------------------------------------------------------------------- /countingderangements/count_derangements_top_down.py: -------------------------------------------------------------------------------- 1 | class CountDerangementsTopDown: 2 | def __init__(self, set_size): 3 | self.set_size = set_size 4 | self.sub_solutions = [-1] * (set_size + 1) 5 | 6 | def count_derangements(self): 7 | return self.count_derangements_rec(self.set_size) 8 | 9 | def count_derangements_rec(self, n): 10 | if self.sub_solutions[n] != -1: 11 | return self.sub_solutions[n] 12 | if n == 1: 13 | return 0 14 | if n == 2: 15 | return 1 16 | result = (n - 1) * (self.count_derangements_rec(n - 1) + self.count_derangements_rec(n - 2)) 17 | self.sub_solutions[n] = result 18 | return result 19 | 20 | 21 | # for i in range(1,64): 22 | for i in range(1,11): 23 | n = CountDerangementsTopDown(i).count_derangements() 24 | print("Derangments in set size {} -> {}".format(i,n)) 25 | -------------------------------------------------------------------------------- /exercise/full_bus_tour.py: -------------------------------------------------------------------------------- 1 | # You are developing an online booking portal for a bus tour company. On the website, tourists can book in 2 | # groups to come on your company's city bus tour. The company has various buses with different capacities. 3 | # You want to determine, from the list of available bookings, if you can completely fill up a particular bus. 4 | # The golden rule is that you cannot break groups apart and put them on separate buses. 5 | # More formally: 6 | # 7 | # For: 8 | # A bus with capacity: int full_cap 9 | # A list of group bookings, where each element represents a group size: int list group_sizes 10 | # Write a function that returns true iff groupSizes contains a subset that when you sum it up is equal to c. 11 | # That is from the list of group bookings you can completely fill up a bus 12 | # 13 | # Assume: 14 | # capacity full_cap is greater or equal to 0 15 | # each integer in groupSizes is greater than 0 16 | # 17 | # Examples: 18 | # groupSizes = {4, 13, 5, 12, 6, 1, 8}, full_cap = 11 should return true as subset (5, 6) has a sum of 9 19 | # groupSizes = {1, 1, 1}, full_cap = 3 should return true as subset (1, 1, 1) has a sum of 3 20 | # groupSizes = {4, 5, 6, 7}, full_cap = 100 should return false 21 | 22 | 23 | class FullBusTour: 24 | def __init__(self, group_sizes, full_cap): 25 | self.group_sizes = group_sizes 26 | self.full_cap = full_cap 27 | 28 | def fits_exactly(self): 29 | return False 30 | 31 | -------------------------------------------------------------------------------- /exercise/solution/full_bus_tour_bottom_up.py: -------------------------------------------------------------------------------- 1 | class FullBusTour: 2 | def __init__(self, group_sizes, full_cap): 3 | self.group_sizes = group_sizes 4 | self.full_cap = full_cap 5 | self.sub_solutions = [[]] * (len(group_sizes) + 1) 6 | for length in range(len(group_sizes) + 1): 7 | self.sub_solutions[length] = [None] * (full_cap + 1) 8 | for c in range(full_cap + 1): 9 | if c == 0: 10 | self.sub_solutions[length][c] = True 11 | elif length == 0: 12 | self.sub_solutions[length][c] = False 13 | else: 14 | c_remaining = c - self.group_sizes[length - 1] 15 | self.sub_solutions[length][c] = self.sub_solutions[length - 1][c] or ( 16 | c_remaining >= 0 and self.sub_solutions[length - 1][c_remaining]) 17 | 18 | def fits_exactly(self): 19 | return self.sub_solutions[len(self.group_sizes)][self.full_cap] 20 | 21 | -------------------------------------------------------------------------------- /exercise/solution/full_bus_tour_rec.py: -------------------------------------------------------------------------------- 1 | class FullBusTour: 2 | def __init__(self, group_sizes, full_cap): 3 | self.group_sizes = group_sizes 4 | self.full_cap = full_cap 5 | 6 | def fits_exactly(self): 7 | return self.fits_exactly_rec(len(self.group_sizes), self.full_cap) 8 | 9 | def fits_exactly_rec(self, length, c): 10 | if c == 0: 11 | return True 12 | if length == 0: 13 | return False 14 | c_remaining = c - self.group_sizes[length - 1] 15 | return self.fits_exactly_rec(length - 1, c) or (c_remaining >= 0 and self.fits_exactly_rec(length - 1, c_remaining)) 16 | 17 | -------------------------------------------------------------------------------- /exercise/solution/full_bus_tour_top_down.py: -------------------------------------------------------------------------------- 1 | class FullBusTour: 2 | def __init__(self, group_sizes, full_cap): 3 | self.group_sizes = group_sizes 4 | self.full_cap = full_cap 5 | self.sub_solutions = [[]] * (len(group_sizes) + 1) 6 | for i in range(len(group_sizes) + 1): 7 | self.sub_solutions[i] = [None] * (full_cap + 1) 8 | 9 | def fits_exactly(self): 10 | return self.fits_exactly_rec(len(self.group_sizes), self.full_cap) 11 | 12 | def fits_exactly_rec(self, length, c): 13 | if c == 0: 14 | return True 15 | if length == 0: 16 | return False 17 | if self.sub_solutions[length][c] is not None: 18 | return self.sub_solutions[length][c] 19 | c_remaining = c - self.group_sizes[length - 1] 20 | result = self.fits_exactly_rec(length - 1, c) or ( 21 | c_remaining >= 0 and self.fits_exactly_rec(length - 1, c_remaining)) 22 | self.sub_solutions[length][c] = result 23 | return result 24 | -------------------------------------------------------------------------------- /justify/text_justify_dyn.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | class TextJustifyDyn: 5 | def __init__(self, txt, line_length): 6 | self.txt = txt 7 | self.line_length = line_length 8 | 9 | def ugly_score(self, txt_length): 10 | if txt_length <= self.line_length: 11 | return (self.line_length - txt_length) ** 2 12 | else: 13 | return sys.maxsize 14 | 15 | def count_chars(self, fr, to): 16 | total_chars = 0 17 | for i in range(fr, to): 18 | total_chars += len(self.txt[i]) 19 | if i < to - 1: 20 | total_chars += 1 21 | return total_chars 22 | 23 | def format_txt(self): 24 | scores = [0] * (len(self.txt) + 1) 25 | ptrs = [0] * len(self.txt) 26 | 27 | for i in range(len(self.txt) - 1, -1, -1): 28 | score = sys.maxsize 29 | for j in range(i + 1, len(self.txt) + 1): 30 | curr_score = self.ugly_score(self.count_chars(i, j)) + scores[j] 31 | if curr_score < score: 32 | score = curr_score 33 | ptrs[i] = j 34 | scores[i] = score 35 | 36 | self.print_txt(ptrs) 37 | return scores[0] 38 | 39 | def print_txt(self, ptrs): 40 | i = 0 41 | while i < len(ptrs): 42 | line = self.txt[i:ptrs[i]] 43 | print(" ".join(line)) 44 | i = ptrs[i] 45 | 46 | 47 | justify = TextJustifyDyn("Isabel sat on the step".split(), 10) 48 | print(justify.format_txt()) 49 | -------------------------------------------------------------------------------- /justify/text_justify_opt.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | class TextJustifyOpt: 5 | def __init__(self, txt, line_length): 6 | self.txt = txt 7 | self.line_length = line_length 8 | self.word_length = [[]] * len(txt) 9 | for i in range(len(txt)): 10 | self.word_length[i] = [0] * len(txt) 11 | self.word_length[i][i] = len(txt[i]) 12 | for j in range(i + 1, len(txt)): 13 | self.word_length[i][j] = self.word_length[i][j - 1] + 1 + len(txt[j]) 14 | for arr in self.word_length: 15 | print(arr) 16 | 17 | def ugly_score(self, txt_length): 18 | if txt_length <= self.line_length: 19 | return (self.line_length - txt_length) ** 2 20 | else: 21 | return sys.maxsize 22 | 23 | def format_txt(self): 24 | scores = [0] * (len(self.txt) + 1) 25 | ptrs = [0] * len(self.txt) 26 | 27 | for i in range(len(self.txt) - 1, -1, -1): 28 | score = sys.maxsize 29 | for j in range(i + 1, len(self.txt) + 1): 30 | curr_score = self.ugly_score(self.word_length[i][j - 1]) + scores[j] 31 | if curr_score < score: 32 | score = curr_score 33 | ptrs[i] = j 34 | scores[i] = score 35 | 36 | self.print_txt(ptrs) 37 | return scores[0] 38 | 39 | def print_txt(self, ptrs): 40 | i = 0 41 | while i < len(ptrs): 42 | line = self.txt[i:ptrs[i]] 43 | print(" ".join(line)) 44 | i = ptrs[i] 45 | 46 | 47 | justify = TextJustifyOpt("Isabel sat on the step".split(), 10) 48 | print(justify.format_txt()) 49 | -------------------------------------------------------------------------------- /justify/text_justify_rec.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | class TextJustifyRec: 5 | def __init__(self, txt, line_length): 6 | self.txt = txt 7 | self.line_length = line_length 8 | 9 | def ugly_score(self, txt_length): 10 | if txt_length <= self.line_length: 11 | return (self.line_length - txt_length) ** 2 12 | else: 13 | return sys.maxsize 14 | 15 | def count_chars(self, fr, to): 16 | total_chars = 0 17 | for i in range(fr, to): 18 | total_chars += len(self.txt[i]) 19 | if i < to - 1: 20 | total_chars += 1 21 | return total_chars 22 | 23 | def format_txt(self, i): 24 | if i == len(self.txt): 25 | return 0 26 | score = sys.maxsize 27 | for x in range(i + 1, len(self.txt) + 1): 28 | line_len = self.count_chars(i, x) 29 | curr_score = self.ugly_score(line_len) 30 | curr_score += self.format_txt(x) 31 | score = min(curr_score, score) 32 | return score 33 | 34 | 35 | justify = TextJustifyRec("Isabel sat on the step".split(), 10) 36 | print(justify.format_txt(0)) 37 | -------------------------------------------------------------------------------- /maxsubarray/max_sub_array_bottom_up.py: -------------------------------------------------------------------------------- 1 | class MaxSubArrayBottomUp: 2 | def __init__(self, prices): 3 | self.prices = prices 4 | self.sub_solutions = [None] * len(prices) 5 | for i in range(len(self.prices)): 6 | if i == 0: 7 | self.sub_solutions[i] = self.prices[0] 8 | else: 9 | self.sub_solutions[i] = max(self.prices[i], self.sub_solutions[i - 1] + self.prices[i]) 10 | 11 | def max_sub_array(self): 12 | max_value = 0 13 | for j in range(len(self.prices)): 14 | max_value = max(max_value, self.sub_solutions[j]) 15 | return max_value 16 | 17 | 18 | msa = MaxSubArrayBottomUp([5, -4, 8, -10, -2, 4, -3, 2, 7, -8, 3, -5, 3]) 19 | print(msa.max_sub_array()) 20 | 21 | # input = [1] * 900 22 | # for i in range(10): 23 | # msa = MaxSubArrayBottomUp(input) 24 | # print(msa.max_sub_array()) 25 | 26 | -------------------------------------------------------------------------------- /maxsubarray/max_sub_array_opt.py: -------------------------------------------------------------------------------- 1 | class MaxSubArrayOpt: 2 | def __init__(self, prices): 3 | self.prices = prices 4 | self.global_max, local_max = 0, 0 5 | for i in range(len(self.prices)): 6 | if i == 0: 7 | local_max = self.prices[0] 8 | else: 9 | local_max = max(self.prices[i], local_max + self.prices[i]) 10 | self.global_max = max(self.global_max, local_max) 11 | 12 | def max_sub_array(self): 13 | return self.global_max 14 | 15 | 16 | msa = MaxSubArrayOpt([5, -4, 8, -10, -2, 4, -3, 2, 7, -8, 3, -5, 3]) 17 | print(msa.max_sub_array()) 18 | 19 | # input = [1] * 900 20 | # for i in range(10): 21 | # msa = MaxSubArrayOpt(input) 22 | # print(msa.max_sub_array()) 23 | 24 | 25 | -------------------------------------------------------------------------------- /maxsubarray/max_sub_array_rec.py: -------------------------------------------------------------------------------- 1 | class MaxSubArrayRec: 2 | def __init__(self, prices): 3 | self.prices = prices 4 | 5 | def max_sub_array(self): 6 | max_value = 0 7 | for j in range(len(self.prices)): 8 | max_value = max(max_value, self.max_sub_array_ending_at(j)) 9 | return max_value 10 | 11 | def max_sub_array_ending_at(self, i): 12 | if i == 0: 13 | return self.prices[0] 14 | return max(self.prices[i], self.max_sub_array_ending_at(i - 1) + self.prices[i]) 15 | 16 | 17 | msa = MaxSubArrayRec([5, -4, 8, -10, -2, 4, -3, 2, 7, -8, 3, -5, 3]) 18 | print(msa.max_sub_array()) 19 | 20 | # input = [1] * 900 21 | # for i in range(10): 22 | # msa = MaxSubArrayRec(input) 23 | # print(msa.max_sub_array()) 24 | 25 | -------------------------------------------------------------------------------- /maxsubarray/max_sub_array_top_down.py: -------------------------------------------------------------------------------- 1 | class MaxSubArrayTopDown: 2 | def __init__(self, prices): 3 | self.prices = prices 4 | self.sub_solutions = [None] * len(prices) 5 | 6 | def max_sub_array(self): 7 | max_value = 0 8 | for j in range(len(self.prices)): 9 | max_value = max(max_value, self.max_sub_array_ending_at(j)) 10 | return max_value 11 | 12 | def max_sub_array_ending_at(self, i): 13 | if self.sub_solutions[i] is not None: 14 | return self.sub_solutions[i] 15 | if i == 0: 16 | return self.prices[0] 17 | m = max(self.prices[i], self.max_sub_array_ending_at(i - 1) + self.prices[i]) 18 | self.sub_solutions[i] = m 19 | return m 20 | 21 | 22 | msa = MaxSubArrayTopDown([5, -4, 8, -10, -2, 4, -3, 2, 7, -8, 3, -5, 3]) 23 | print(msa.max_sub_array()) 24 | 25 | # input = [1] * 900 26 | # for i in range(10): 27 | # msa = MaxSubArrayTopDown(input) 28 | # print(msa.max_sub_array()) 29 | 30 | -------------------------------------------------------------------------------- /recursion/file_search.py: -------------------------------------------------------------------------------- 1 | import os 2 | from os.path import isfile, join 3 | 4 | 5 | def search(full_filepath, filename): 6 | print("checking: " + full_filepath) 7 | if filename in full_filepath: 8 | print("Found " + filename + " in " + full_filepath) 9 | return True 10 | if isfile(full_filepath): 11 | return False 12 | for file in os.listdir(full_filepath): 13 | found = search(join(full_filepath, file), filename) 14 | if found: 15 | return True 16 | return False 17 | 18 | 19 | search("C:\\tools", "gawk.sh") 20 | -------------------------------------------------------------------------------- /recursion/timer.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | def countdown(n): 5 | if n < 0: 6 | return 7 | print(n) 8 | time.sleep(1) 9 | countdown(n - 1) 10 | 11 | 12 | countdown(5) 13 | -------------------------------------------------------------------------------- /strdistance/string_distance_bottom_up.py: -------------------------------------------------------------------------------- 1 | class StringDistanceBottomUp: 2 | def __init__(self, str_A, str_B): 3 | self.str_A = str_A 4 | self.str_B = str_B 5 | self.dist = [[]] * (len(str_A) + 1) 6 | for a in range(len(str_A) + 1): 7 | self.dist[a] = [-1] * (len(str_B) + 1) 8 | for b in range(len(str_B) + 1): 9 | if a == 0: 10 | self.dist[a][b] = b 11 | elif b == 0: 12 | self.dist[a][b] = a 13 | else: 14 | replace_cost = 0 if self.str_A[a - 1] == self.str_B[b - 1] else 1 15 | cost_delete = self.dist[a - 1][b] + 1 16 | cost_insert = self.dist[a][b - 1] + 1 17 | cost_replace = self.dist[a - 1][b - 1] + replace_cost 18 | self.dist[a][b] = min(cost_delete, cost_insert, cost_replace) 19 | print(self.dist[a]) 20 | 21 | def distance(self): 22 | return self.dist[len(self.str_A)][len(self.str_B)] 23 | 24 | 25 | #dist = StringDistanceBottomUp("TodayIsSaturday", "TomorrowIsSunday") 26 | dist = StringDistanceBottomUp("Saturday", "Sundays") 27 | print(dist.distance()) 28 | -------------------------------------------------------------------------------- /strdistance/string_distance_opt.py: -------------------------------------------------------------------------------- 1 | class StringDistanceOpt: 2 | def __init__(self, str_A, str_B): 3 | self.str_A = str_A 4 | self.str_B = str_B 5 | self.dist_read = [-1] * (len(str_B) + 1) 6 | self.dist_write = [-1] * (len(str_B) + 1) 7 | 8 | for a in range(len(str_A) + 1): 9 | for b in range(len(str_B) + 1): 10 | if a == 0: 11 | self.dist_write[b] = b 12 | elif b == 0: 13 | self.dist_write[b] = a 14 | else: 15 | replace_cost = 0 if self.str_A[a - 1] == self.str_B[b - 1] else 1 16 | cost_delete = self.dist_read[b] + 1 17 | cost_insert = self.dist_write[b - 1] + 1 18 | cost_replace = self.dist_read[b - 1] + replace_cost 19 | self.dist_write[b] = min(cost_delete, cost_insert, cost_replace) 20 | (self.dist_read, self.dist_write) = (self.dist_write, self.dist_read) 21 | print(self.dist_read) 22 | 23 | def distance(self): 24 | return self.dist_read[len(self.str_B)] 25 | 26 | 27 | #dist = StringDistanceOpt("TodayIsSaturday", "TomorrowIsSunday") 28 | dist = StringDistanceOpt("Saturday", "Sundays") 29 | print(dist.distance()) 30 | -------------------------------------------------------------------------------- /strdistance/string_distance_rec.py: -------------------------------------------------------------------------------- 1 | class StringDistance: 2 | def __init__(self, str_A, str_B): 3 | self.str_A = str_A 4 | self.str_B = str_B 5 | 6 | def distance(self): 7 | return self.distance_r(len(self.str_A), len(self.str_B)) 8 | 9 | def distance_r(self, a, b): 10 | if a == 0: 11 | return b 12 | if b == 0: 13 | return a 14 | replace_cost = 0 if self.str_A[a - 1] == self.str_B[b - 1] else 1 15 | 16 | cost_delete = self.distance_r(a - 1, b) + 1 17 | cost_insert = self.distance_r(a, b - 1) + 1 18 | cost_replace = self.distance_r(a - 1, b - 1) + replace_cost 19 | return min(cost_delete, cost_insert, cost_replace) 20 | 21 | 22 | #dist = StringDistance("TodayIsSaturday", "TomorrowIsSunday") 23 | dist = StringDistance("Saturday", "Sundays") 24 | print(dist.distance()) 25 | -------------------------------------------------------------------------------- /strdistance/string_distance_top_down.py: -------------------------------------------------------------------------------- 1 | class StringDistanceTopDown: 2 | def __init__(self, str_A, str_B): 3 | self.str_A = str_A 4 | self.str_B = str_B 5 | self.dist = [[]] * (len(str_A) + 1) 6 | for i in range(len(str_A) + 1): 7 | self.dist[i] = [-1] * (len(str_B) + 1) 8 | 9 | def distance(self): 10 | return self.distance_r(len(self.str_A), len(self.str_B)) 11 | 12 | def distance_r(self, a, b): 13 | if self.dist[a][b] != -1: 14 | return self.dist[a][b] 15 | if a == 0: 16 | return b 17 | if b == 0: 18 | return a 19 | replace_cost = 0 if self.str_A[a - 1] == self.str_B[b - 1] else 1 20 | 21 | cost_delete = self.distance_r(a - 1, b) + 1 22 | cost_insert = self.distance_r(a, b - 1) + 1 23 | cost_replace = self.distance_r(a - 1, b - 1) + replace_cost 24 | min_cost = min(cost_delete, cost_insert, cost_replace) 25 | self.dist[a][b] = min_cost 26 | return min_cost 27 | 28 | 29 | #dist = StringDistanceTopDown("TodayIsSaturday", "TomorrowIsSunday") 30 | dist = StringDistanceTopDown("Saturday", "Sundays") 31 | print(dist.distance()) 32 | -------------------------------------------------------------------------------- /test/test_full_bus_tour.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from exercise.full_bus_tour import FullBusTour 4 | 5 | 6 | class Evaluate(TestCase): 7 | def test_simple_bus_full(self): 8 | full_bus_tour = FullBusTour([3, 34, 4, 12, 5, 2], 9) 9 | assert full_bus_tour.fits_exactly(), "[3, 34, 4, 12, 5, 2] with a bus capacity of 9 should fit with [4, 5]" 10 | 11 | def test_simple_bus_full_again(self): 12 | full_bus_tour = FullBusTour([3, 34, 4, 12, 5, 2], 10) 13 | assert full_bus_tour.fits_exactly(), "[3, 34, 4, 12, 5, 2] with a bus capacity of 10 should fit with [3, 5, 2]" 14 | 15 | def test_small_bus_non_full(self): 16 | full_bus_tour = FullBusTour([3, 34, 4, 12, 5, 2], 1) 17 | assert not full_bus_tour.fits_exactly(), "{3, 34, 4, 12, 5, 2} with a bus capacity of 1 should not fit" 18 | 19 | def test_ones_twos_bus_non_full(self): 20 | full_bus_tour = FullBusTour([1, 2, 5, 8, 7, 12], 4) 21 | assert not full_bus_tour.fits_exactly(), "[1, 2, 5, 8, 7, 12] with a bus capacity of 4 should not fit" 22 | 23 | def test_even_only_bus_non_full(self): 24 | full_bus_tour = FullBusTour([2, 4, 2, 8, 6, 4, 10, 6], 21) 25 | assert not full_bus_tour.fits_exactly(), "[2, 4, 2, 8, 6, 4, 10, 6] with a bus capacity of 21 should not fit" 26 | 27 | def test_odd_only_bus_full(self): 28 | full_bus_tour = FullBusTour([1, 3, 5, 1, 7, 11, 9, 9], 21) 29 | assert full_bus_tour.fits_exactly(), "[1, 3, 5, 1, 7, 11, 9, 9] with a bus capacity of 21 should fit with [11, 9, 1]" 30 | 31 | def test_big_bus_full(self): 32 | full_bus_tour = FullBusTour([1, 3, 5, 4, 7, 11, 9, 9, 21, 7, 4, 21, 13, 8, 2, 5, 1, 18], 107) 33 | assert full_bus_tour.fits_exactly(), "[1, 3, 5, 4, 7, 11, 9, 9, 21, 7, 4, 21, 13, 8, 2, 5, 1, 18] with a bus capacity of 107 should fit" 34 | 35 | def testz_perf_test(self): 36 | full_bus_tour = FullBusTour([1] * 500, 500) 37 | assert full_bus_tour.fits_exactly(), "Performance test is failing. Improve your algorithm!" 38 | --------------------------------------------------------------------------------