From f7e7ea06dee724ee9251c17e22ae81862387e6a4 Mon Sep 17 00:00:00 2001 From: romarowski Date: Sat, 24 Feb 2024 18:53:47 -0500 Subject: [PATCH] Add dynamic programming solutions --- .vscode/settings.json | 11 ++++ 01-string.md | 3 ++ 03-binary_tree.md | 27 ++++++++++ ...rogramming.md => 05-dynamic_programming.md | 25 +++++++++ __init__.py | 0 dynamic_programming/sum_possible.py | 50 ++++++++++++------ string_array/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 143 bytes ..._common_divisor_of_strings.cpython-310.pyc | Bin 0 -> 1000 bytes {string => string_array}/compress.py | 0 {string => string_array}/five_sort.py | 0 .../greatest_common_divisor_of_strings.py | 44 +++++++++++++++ .../linked_list_values.py | 0 string_array/merge_strings_alternatively.py | 38 +++++++++++++ string_array/tests/__init__.py | 1 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 164 bytes ..._common_divisor_of_strings.cpython-310.pyc | Bin 0 -> 1612 bytes ...test_greatest_common_divisor_of_strings.py | 41 ++++++++++++++ {string => string_array}/uncompress.py | 0 {string => string_array}/zipper_lists.py | 0 20 files changed, 224 insertions(+), 16 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 01-string.md create mode 100644 03-binary_tree.md rename 05-dynamic-programming.md => 05-dynamic_programming.md (56%) create mode 100644 __init__.py create mode 100644 string_array/__init__.py create mode 100644 string_array/__pycache__/__init__.cpython-310.pyc create mode 100644 string_array/__pycache__/greatest_common_divisor_of_strings.cpython-310.pyc rename {string => string_array}/compress.py (100%) rename {string => string_array}/five_sort.py (100%) create mode 100644 string_array/greatest_common_divisor_of_strings.py rename {string => string_array}/linked_list_values.py (100%) create mode 100644 string_array/merge_strings_alternatively.py create mode 100644 string_array/tests/__init__.py create mode 100644 string_array/tests/__pycache__/__init__.cpython-310.pyc create mode 100644 string_array/tests/__pycache__/test_greatest_common_divisor_of_strings.cpython-310.pyc create mode 100644 string_array/tests/test_greatest_common_divisor_of_strings.py rename {string => string_array}/uncompress.py (100%) rename {string => string_array}/zipper_lists.py (100%) diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..184c4eb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "python.testing.unittestArgs": [ + "-v", + "-s", + ".", + "-p", + "test_*.py" + ], + "python.testing.pytestEnabled": false, + "python.testing.unittestEnabled": true +} \ No newline at end of file diff --git a/01-string.md b/01-string.md new file mode 100644 index 0000000..3678a2f --- /dev/null +++ b/01-string.md @@ -0,0 +1,3 @@ +## String + + diff --git a/03-binary_tree.md b/03-binary_tree.md new file mode 100644 index 0000000..5b0d4c9 --- /dev/null +++ b/03-binary_tree.md @@ -0,0 +1,27 @@ +## Binary Tree + +### Tree height problem + +**Statement**: Given a binary tree, find its height. + +**Approach**: Transverse the tree, keeping track of the height of the left and right subtrees. Return the maximum height. +1. Base case: empty (or null) tree has height -1. +2. Recursive case: return 1 + the maximum height of the left and right subtrees. + + +```python +def height(node): + + # empty tree + if node is None: + return -1 + + # recursive case + return 1 + max(height(node.left), height(node.right)) +``` + +**Complexity**: +- Time: O($n$), need to transverse every node on the tree. +- Space: O($n$), we have n calls to ```height()```. + + diff --git a/05-dynamic-programming.md b/05-dynamic_programming.md similarity index 56% rename from 05-dynamic-programming.md rename to 05-dynamic_programming.md index cbcfda3..91347e0 100644 --- a/05-dynamic-programming.md +++ b/05-dynamic_programming.md @@ -1,5 +1,10 @@ ## Dynamic Programming +## Concepts I don't understand: + +- [ ] Time complexity in brute force sum possible?? + + ### Fibonacci sequence problem **Statement:** Given a number ```n``` $\in\N$, find the its Fibonacci number. @@ -33,4 +38,24 @@ A better approach is to use **dynamic programming** and store the results of the - Time: $O(n)$ - Space: $O(n)$ +### Sum possible problem + +**Statement:** Given a list of numbers $\text{nums}=[n_1, n_2, ..., n_n]$ and a target amount $a$, find if it is possible to obtain the target sum using the numbers in the list. + +**Approach**: +Is possible to solve this problem with brute force recursion. The idea is to substract all possible combinations of ```nums``` from ```a``` until reaching 0. + +1. Code the two base cases: + a. Amount is 0, then we succesfully got to $a$. + b. + + +```python +def sum_possible(a, nums): + if a < 0: + return False + if a == 0: + return True + + diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dynamic_programming/sum_possible.py b/dynamic_programming/sum_possible.py index ab0181e..91e0f3e 100644 --- a/dynamic_programming/sum_possible.py +++ b/dynamic_programming/sum_possible.py @@ -8,34 +8,52 @@ You may assume that the target amount is non-negative. """ -def sum_possible(amount, numbers): - for n in numbers: - possible = recur(amount, n, numbers) - if possible: - return True +############################## +# dynamic programming O(a*n) # +############################## - return False +def sum_possible(amount, numbers): + calculated = dict() + return _sum_it(amount, numbers, calculated) -def recur(remainder, subtrahend, numbers): - - if subtrahend > remainder: - return +def _sum_it(amount, numbers, calculated): + if amount < 0: + return False - remainder = remainder - subtrahend - - if remainder == 0: + if amount == 0: return True + if amount in calculated: + return calculated[amount] + + for n in numbers: + if _sum_it(amount-n, numbers, calculated): + calculated[amount] = True + return True + calculated[amount] = False + return False - return False - +print(sum_possible(4, [1, 2, 3,])) # -> True, 4 + 4 +################################# +# brute force recursion O(n**a) # +################################# +#def sum_possible(amount, numbers): +# if amount < 0: +# return False +# +# if amount == 0: +# return True +# for n in numbers: +# if sum_possible(amount-n, numbers): +# return True +# +# return False -print(sum_possible(4, [1, 2, 3,])) # -> True, 4 + 4 ############################### # solution with n! complexity # diff --git a/string_array/__init__.py b/string_array/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/string_array/__pycache__/__init__.cpython-310.pyc b/string_array/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6a9707b8508f680a9849fc0e9b55bc4066033cf GIT binary patch literal 143 zcmd1j<>g`k0&%9BsUZ3>h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vAKO;XkRX-^; zPd_=ow5T|>SiiWWs5H5x5=3X_rN<{06(v^c$H!;pWtPOp>lIYq;;_lhPbtkwwF8+| J%mgG@7y$QIAGH7g literal 0 HcmV?d00001 diff --git a/string_array/__pycache__/greatest_common_divisor_of_strings.cpython-310.pyc b/string_array/__pycache__/greatest_common_divisor_of_strings.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6b5340e60d71037d22067251bdac1052fdd4ded GIT binary patch literal 1000 zcmY*XOK%e~5VpNeve`bMr9$E%Vf92H+R$F;0T6A03JH}%4oF;9+qILdlC>+_X^6Br z1%3=y{tdoz>K{OY17f@(Y4PrS-fteB8Ski#jR?W|MZb$16rmrUTwkC=FG0)!07oAY z$DDjdAF&`j7!rL~1Oju)YjZNk7*(v!8w=TR3I2(W|wLq4&pDD-*E?0-d zQAXgtmun3^{Es*WG2Z|zD$yLz2<#a4pd@85tm&;1&uEEeHR+K8tCe(C2QLaYjeEN-bS;q3_(xyTkCZz%RQf=S;7fra|aE{EGi@LL1q0ab`e+7E`qbk0fHQq z!}k|*$LFu|nFl<85um4+P&m4TIY17zhzmwSx*(UfwdSbrzQTQn3x}m^_{)K*aCD+H zcbmqhHqVl!Hps@>HMJb$Yqj5^^%33iO;8Qy`S;-z3yTZH{pEq2q47p5Q^)V0#f4n zO^_YDjwv7o>pBj9(lvd{cOE8*uQ^Fv{qX1*UKe-(;vYv2k4{_EHIjs@EJ;=|GA#te l)Xt%7TXlC0wyT4m6^f5X;)TCpvkpK}xDnpp#M_wu`3u?l4~zf+ literal 0 HcmV?d00001 diff --git a/string/compress.py b/string_array/compress.py similarity index 100% rename from string/compress.py rename to string_array/compress.py diff --git a/string/five_sort.py b/string_array/five_sort.py similarity index 100% rename from string/five_sort.py rename to string_array/five_sort.py diff --git a/string_array/greatest_common_divisor_of_strings.py b/string_array/greatest_common_divisor_of_strings.py new file mode 100644 index 0000000..2460c73 --- /dev/null +++ b/string_array/greatest_common_divisor_of_strings.py @@ -0,0 +1,44 @@ +""" +1071. Greatest Common Divisor of Strings + +Easy + +For two strings s and t, we say "t divides s" if and only +if s = t + t + t + ... + t + t (i.e., t is concatenated with +itself one or more times). + +Given two strings str1 and str2, return the largest string x +such that x divides both str1 and str2. +""" + +def gcdOfStrings(str1, str2): + m = len(str1) + n = len(str2) + + if m>=n: + divisor = str2 + word = str1 + else: + divisor = str1 + word = str2 + + static_divisor = divisor + cur = len(divisor) + while cur > 0: + remainder = len(word) % len(divisor) + if remainder==0: + k = int((len(word)/len(divisor))) + divisable = word == divisor * k + if divisable: + if len(static_divisor) % len(divisor) == 0: + return divisor + + cur-=1 + divisor = divisor[:cur] + return "" + + + +if __name__ == "__main__": + print(gcdOfStrings("ABCABC", "ABC")) # -> "ABC" + print(gcdOfStrings("ABABAB", "AB")) # -> "AB" \ No newline at end of file diff --git a/string/linked_list_values.py b/string_array/linked_list_values.py similarity index 100% rename from string/linked_list_values.py rename to string_array/linked_list_values.py diff --git a/string_array/merge_strings_alternatively.py b/string_array/merge_strings_alternatively.py new file mode 100644 index 0000000..6f798b5 --- /dev/null +++ b/string_array/merge_strings_alternatively.py @@ -0,0 +1,38 @@ +""" +1768. Merge Strings Alternately + +Easy + +You are given two strings word1 and word2. +Merge the strings by adding letters in alternating order, +starting with word1. If a string is longer than the other, +append the additional letters onto the end of the merged string. + +Return the merged string. +""" + +def mergeAlternatively(word1, word2): + cur1 = 0 + cur2 = 0 + ans = "" + + while cur1 < len(word1) and cur2 < len(word2): + ans += word1[cur1] + word2[cur2] + + cur1+=1 + cur2+=1 + + if cur1 == len(word1): + if cur2 < len(word2): + ans+=word2[cur2:] + return ans + + if cur2 == len(word2): + if cur1 < len(word1): + ans+=word1[cur1:] + return ans + return ans + + + +print(mergeAlternatively("abc","pqr")) #-> "apbqcr" \ No newline at end of file diff --git a/string_array/tests/__init__.py b/string_array/tests/__init__.py new file mode 100644 index 0000000..bb1fbc2 --- /dev/null +++ b/string_array/tests/__init__.py @@ -0,0 +1 @@ +import sys \ No newline at end of file diff --git a/string_array/tests/__pycache__/__init__.cpython-310.pyc b/string_array/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7415f07437c02398acfcece57118ff9624350739 GIT binary patch literal 164 zcmd1j<>g`kf>7q0soX&NF^GcG6q0MTwRAC8@EazbW01p5r=>Px# literal 0 HcmV?d00001 diff --git a/string_array/tests/__pycache__/test_greatest_common_divisor_of_strings.cpython-310.pyc b/string_array/tests/__pycache__/test_greatest_common_divisor_of_strings.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf13754fafb5d4f97578072670d84d296c62d725 GIT binary patch literal 1612 zcma)6PjAyO6t|tE%eI#N5fp|5f+2(oVilYxv@AormBWS@xkM%QMp4rgJ3;HziNp3m z;J9zW*T7d!oOk2Idk!7hNK~Bo=REuQz2AF|U(#sI5on*C@8X+H$PW~X4d%!OboCg3 z6HfaipfIvNi><(-WSek{+oy!vf_|`1tia)PhqRoZpieq2s_O?4f3?4p%HH5WML1h* z41^8nY8^lr+X3U0vr`gSx#hs-4zGdi@EPvHSmU#N4#pWi&+9O{ya67s>BU{4(rth1 zuQv)tCk-QUp!?9(DgdX);OCe@x5nfPN$Hr5*$Eq4Um1m1!KT&GHE+|0S6cvQOwV_f z5;A@HG3)jvl0UPxtwetxFqO{?KGzH4a44cw@G!~JVV0f|*#O;NZ`$vZShU}XK|4w^ zsf237t}IGNI6{KLu9V$TJB2`$!SFzeE{b84#BnkRdGAwCB{EF*!+eWc8;SYt_4>suZ4IN}*MmLffR!+w?Xy^S3mS$VVa{lad^+fvB5? z#UQy7X&qG;0gfJ(^ZsYYe{iO7?6i_I>&WZqwUX+xp(^5GI+~oq@&(NYR{p6~lpPIk#8HcsRuvFhOkq1E!t7L_}#S?z}501qbA zb72@KJnQ3kJq)pC<{~ yW`kae{58u`eW{|XO)n#+nrRnf^t({w&|!gu;biWqyPgO20!xD_FD|Lmru7?KzmL%X literal 0 HcmV?d00001 diff --git a/string_array/tests/test_greatest_common_divisor_of_strings.py b/string_array/tests/test_greatest_common_divisor_of_strings.py new file mode 100644 index 0000000..eea5f0f --- /dev/null +++ b/string_array/tests/test_greatest_common_divisor_of_strings.py @@ -0,0 +1,41 @@ +import unittest as ut +from string_array.greatest_common_divisor_of_strings import gcdOfStrings + + +class TestGCDOfStrings(ut.TestCase): + def test_common_divisor_exists(self): + str1 = "ABCABC" + str2 = "ABC" + expected_output = "ABC" + self.assertEqual(gcdOfStrings(str1, str2), expected_output) + + def test_no_common_divisor(self): + str1 = "ABABAB" + str2 = "CD" + expected_output = "" + self.assertEqual(gcdOfStrings(str1, str2), expected_output) + + def test_empty_strings(self): + str1 = "" + str2 = "" + expected_output = "" + self.assertEqual(gcdOfStrings(str1, str2), expected_output) + + def test_same_string(self): + str1 = "XYZ" + str2 = "XYZ" + expected_output = "XYZ" + self.assertEqual(gcdOfStrings(str1, str2), expected_output) + + def test_long_string(self): + str1 = "TAUXX" * 5 + str2 = "TAUXX" * 9 + expected_output = "TAUXX" + self.assertEqual(gcdOfStrings(str1, str2), expected_output) + + + def test_more_strings(self): + str1 = "AAAAAAAAA" + str2 = "AAACCC" + expected_output = "" + self.assertEqual(gcdOfStrings(str1, str2), expected_output) \ No newline at end of file diff --git a/string/uncompress.py b/string_array/uncompress.py similarity index 100% rename from string/uncompress.py rename to string_array/uncompress.py diff --git a/string/zipper_lists.py b/string_array/zipper_lists.py similarity index 100% rename from string/zipper_lists.py rename to string_array/zipper_lists.py