diff --git a/best-time-to-buy-and-sell-stock/dolphinflow86.py b/best-time-to-buy-and-sell-stock/dolphinflow86.py new file mode 100644 index 0000000000..c2df5e394d --- /dev/null +++ b/best-time-to-buy-and-sell-stock/dolphinflow86.py @@ -0,0 +1,13 @@ +# 1) Keep track of local minimum and use local minimum to update max profile while interating prices. +# TC: O(N) where N is the length of prices +# SC: O(1) +class Solution: + def maxProfit(self, prices: List[int]) -> int: + min_price = prices[0] + max_profit = 0 + + for price in prices: + max_profit = max(max_profit, price - min_price) + min_price = min(min_price, price) + + return max_profit diff --git a/encode-and-decode-strings/dolphinflow86.py b/encode-and-decode-strings/dolphinflow86.py new file mode 100644 index 0000000000..1fadd25f99 --- /dev/null +++ b/encode-and-decode-strings/dolphinflow86.py @@ -0,0 +1,30 @@ +# 1) Prepend each word with its length and a delimiter '%'. +# TC: encode O(N) where N is the len(str), decode O(N) where N is the len(s) +# SC: O(N) for storing the encoded string +class Solution: + + def encode(self, strs: list[str]) -> str: + answer = "" + for s in strs: + answer += f"{len(s)}%{s}" + return answer + + def decode(self, s: str) -> list[str]: + left = 0 + right = 0 + str_len = len(s) + + result = [] + while right < str_len: + while s[right] != "%": + right += 1 + + num_len = int(s[left:right]) + start = right + 1 + word = s[start : start + num_len] + result.append(word) + + left = start + num_len + right = left + + return result diff --git a/group-anagrams/dolphinflow86.py b/group-anagrams/dolphinflow86.py new file mode 100644 index 0000000000..ad9ee569be --- /dev/null +++ b/group-anagrams/dolphinflow86.py @@ -0,0 +1,13 @@ +# 1) Group words by their sorted form using defaultdict. While iterating the strs, sort each word and append original word to the corresponding list. After then convert dict to 2 dimensional list and return the list. +# TC: O(N*LlogL) where N is length of strs, L is max length of a word. +# SC: O(N*L) + +class Solution: + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + groups = defaultdict(list) + + for str in strs: + sorted_str = "".join(sorted(str)) + groups[sorted_str].append(str) + + return list(groups.values()) diff --git a/implement-trie-prefix-tree/dolphinflow86.py b/implement-trie-prefix-tree/dolphinflow86.py new file mode 100644 index 0000000000..73639f290a --- /dev/null +++ b/implement-trie-prefix-tree/dolphinflow86.py @@ -0,0 +1,53 @@ +# 1) Tried to come up with the Trie data structure first and then imlement TrieNode. Key factor here is each TrieNode has children array and is_end to connect to its child nodes and end flag. +# TC: insert, search O(N) where N is len(word), prefix O(L) where L is len(prefix) +# SC: insert O(N) where N is len(word), search/startsWith O(1) +class TrieNode: + def __init__(self): + self.children = [None] * 26 + self.is_end = False + +class Trie: + + def __init__(self): + self.root = TrieNode() + + # apple + def insert(self, word: str) -> None: + cur = self.root + + for c in word: + idx = ord(c) - ord('a') + if not cur.children[idx]: + cur.children[idx] = TrieNode() + cur = cur.children[idx] + + cur.is_end = True + + # apple + def search(self, word: str) -> bool: + cur = self.root + + for c in word: + idx = ord(c) - ord('a') + if cur.children[idx]: cur = cur.children[idx] + else: + return False + + return cur.is_end + + def startsWith(self, prefix: str) -> bool: + cur = self.root + + for c in prefix: + idx = ord(c) - ord('a') + if cur.children[idx]: cur = cur.children[idx] + else: return False + + return True + + +# Your Trie object will be instantiated and called as such: +# obj = Trie() +# obj.insert(word) +# param_2 = obj.search(word) +# param_3 = obj.startsWith(prefix) diff --git a/word-break/dolphinflow86.py b/word-break/dolphinflow86.py new file mode 100644 index 0000000000..3052926284 --- /dev/null +++ b/word-break/dolphinflow86.py @@ -0,0 +1,16 @@ +# 1) I couldn't figure it out by myself this time, so I looked into the solution and found the DP approach. Key point here is that when an element of dp is True, use it as a checkpoint to slice the rest of the string. +# TC: O(N^3) where N is len(s) +# SC: O(N + L) where N is len(s), L is total length of characters in wordDict +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + word_set = set(wordDict) + s_len = len(s) + dp = [False] * (s_len + 1) + dp[0] = True + + for i in range(1, s_len + 1): + for j in range(i): + if dp[j] and s[j:i] in word_set: + dp[i] = True + + return dp[-1]