diff --git a/best-time-to-buy-and-sell-stock/dusunax.py b/best-time-to-buy-and-sell-stock/dusunax.py new file mode 100644 index 000000000..93687418f --- /dev/null +++ b/best-time-to-buy-and-sell-stock/dusunax.py @@ -0,0 +1,31 @@ +''' +# 121. Best Time to Buy and Sell Stock + +use **bottom-up dynamic programming** to solve the problem. + +## Time and Space Complexity +``` +TC: O(n) +SC: O(1) +``` + +## TC is O(n): +- iterating through the list just once to find the maximum profit. = O(n) + +## SC is O(1): +- using two variables to store the minimum price and maximum profit. = O(1) +''' +class Solution: + def maxProfit(self, prices: List[int]) -> int: + if len(prices) == 1: + return 0 + + lowest_price = prices[0] # SC: O(1) + max_profit = 0 # SC: O(1) + + for price in prices: # TC: O(n) + lowest_price = min(price, lowest_price) + curr_profit = price - lowest_price + max_profit = max(curr_profit, max_profit) + + return max_profit diff --git a/encode-and-decode-strings/dusunax.py b/encode-and-decode-strings/dusunax.py new file mode 100644 index 000000000..4a45f83e7 --- /dev/null +++ b/encode-and-decode-strings/dusunax.py @@ -0,0 +1,65 @@ +''' +# 271. Encode and Decode Strings + +## Time and Space Complexity + +Use ":" as a separator and also store the length of each string to decode the string correctly. + + +### encode + +``` +TC: O(n * k) +SC: O(m) +``` + +#### TC is O(n * k): + +- iterating through the list of strings and appending each string to the result. = O(n * k) + - f-string is O(k) + +#### SC is O(m): +- storing the result in a string. + +### decode + +``` +TC: O(m) +SC: O(m) +``` + +#### TC is O(m): +- iterating over the string until the string length is 0. = O(m) +- do list slicings for extract parts and removing the processed section = each operation takes O(k) + +#### SC is O(m): +- storing the result in a list(total length of strings is m) = O(m) + +''' + +class Solution: + """ + @param: strs: a list of strings + @return: encodes a list of strings to a single string. + """ + def encode(self, strs): + result = '' + for str in strs: # TC: O(n) + result += f"{len(str)}:{str}" # TC: O(k) + return result + + """ + @param: str: A string + @return: decodes a single string to a list of strings + """ + def decode(self, str): + result = [] + + while len(str) > 0: # TC: O(m) + length = int(str[:1]) # TC: O(k) + string = str[2:length+2] # TC: O(k) + str = str[length+2:] # TC: O(k) + + result.append(string) # SC: O(m) + + return result diff --git a/group-anagrams/dusunax.py b/group-anagrams/dusunax.py new file mode 100644 index 000000000..8eb6efde1 --- /dev/null +++ b/group-anagrams/dusunax.py @@ -0,0 +1,27 @@ +''' +# 49. Group Anagrams + +use **hash map** to solve the problem. + +## Time and Space Complexity +``` +TC: O(N * K * Log(K)) +SC: O(N * K) +``` + +## TC is O(N * K * Log(K)): +- iterating through the list of strings and sorting each string. = O(N * K * Log(K)) + +## SC is O(N * K): +- using a hash map to store the sorted strings. = O(N * K) +''' +class Solution: + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + result = defaultdict(list) + + for str in strs: # TC: O(N) + sorted_key = ''.join(sorted(str)) # sorting 👉 TC: O(K * Log(K)) + result[sorted_key].append(str) # TC: O(1) on average + + return list(result.values()) + diff --git a/implement-trie-prefix-tree/dusunax.py b/implement-trie-prefix-tree/dusunax.py new file mode 100644 index 000000000..a2cc1bf5b --- /dev/null +++ b/implement-trie-prefix-tree/dusunax.py @@ -0,0 +1,49 @@ +''' +# 208. Implement Trie (Prefix Tree) + +``` +- Node structure +{children: {char: {children: { ... }, is_end: False}}, is_end: False} +``` + +## Time and Space Complexity + +``` +TC: O(n) +SC: O(n) +``` + +#### TC is O(n): +- insert, search, startsWith: iterating through the word just once. = O(n) + +#### SC is O(n): +- insert, search, startsWith: using a hash map to store the children nodes. = O(n) +''' + +class Trie: + def __init__(self): + self.root = {"children": {}, "is_end": False} + + def insert(self, word: str) -> None: + node = self.root + for char in word: + if char not in node["children"]: + node["children"][char] = {"children": {}, "is_end": False} + node = node["children"][char] + node["is_end"] = True + + def search(self, word: str) -> bool: + node = self.root + for char in word: + if char not in node["children"]: + return False + node = node["children"][char] + return node["is_end"] + + def startsWith(self, prefix: str) -> bool: + node = self.root + for char in prefix: + if char not in node["children"]: + return False + node = node["children"][char] + return True diff --git a/word-break/dusunax.py b/word-break/dusunax.py new file mode 100644 index 000000000..a1ba4f996 --- /dev/null +++ b/word-break/dusunax.py @@ -0,0 +1,38 @@ +''' +# 139. Word Break + +use dynamic programming to check if the string is segmentable. + +> **use dp to avoid redundant computations:** +> backtracking approach will check all possible combinations of words recursively, which has time complexity of O(2^n)) + +## Time and Space Complexity + +``` +TC: O(n^2) +SC: O(n) +``` + +#### TC is O(n^2): +- nested loops for checking if the string is segmentable. = O(n^2) + - outer loop: iterate each char index from the start to the end. = O(n) + - inner loop: for each index in the outer loop, checks substrings within the range of valid words in wordDict. = worst case, O(n) + +#### SC is O(n): +- using a dp array to store whether index i is segmentable. = O(n) +''' +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + word_set = set(wordDict) + n = len(s) + + segment_dp = [False] * (n + 1) # SC: O(n) + segment_dp[0] = True # Base case: an empty string is segmentable + + for end in range(1, n + 1): # TC: O(n^2) + for start in range(end): + if segment_dp[start] and s[start:end] in word_set: + segment_dp[end] = True + break + + return segment_dp[n]