diff --git a/clone-graph/yolophg.py b/clone-graph/yolophg.py new file mode 100644 index 000000000..1dbb91b95 --- /dev/null +++ b/clone-graph/yolophg.py @@ -0,0 +1,28 @@ +# Time Complexity: O(N + E) - visit each node once and for each node, we iterate through its neighbors O(E). +# Space Complexity: O(N) - store a copy of each node in the hashmap O(N). + +class Solution: + def cloneGraph(self, node): + if node is None: + return None + + # dictionary to keep track of cloned nodes (original -> clone) + mp = {} + + def clone(node): + if node in mp: + # if the node has already been cloned, return the copy + return mp[node] + + # create a new node with the same value + cpy = Node(node.val) + # store it in the map so don't clone it again + mp[node] = cpy + + # clone all neighbors and add them to the new node's neighbors list + for n in node.neighbors: + cpy.neighbors.append(clone(n)) + + return cpy + + return clone(node) diff --git a/longest-common-subsequence/yolophg.py b/longest-common-subsequence/yolophg.py new file mode 100644 index 000000000..8e8e4b6fc --- /dev/null +++ b/longest-common-subsequence/yolophg.py @@ -0,0 +1,29 @@ +# Time Complexity: O(m * n) - each (i, j) pair is computed once and stored, reducing redundant calls. +# Space Complexity: O(m * n) - memoization dictionary stores O(m * n) states. +# - Recursion stack depth is O(m + n) in the worst case. + +class Solution: + def longestCommonSubsequence(self, t1: str, t2: str) -> int: + # to memoize results so we don't recompute the same subproblems + m = dict() + + # recursive function to compute LCS + def s(i, j): + # base case: if we reach the end of either string, there's nothing left to compare + if i == len(t1) or j == len(t2): + return 0 + + # if already computed this state, just return the cached value + if (i, j) in m: + return m[(i, j)] + + # if the characters match, we take this character and move diagonally + if t1[i] == t2[j]: + m[i, j] = 1 + s(i + 1, j + 1) + else: + # if they don't match, we either move forward in t1 or t2 and take the max + m[i, j] = max(s(i + 1, j), s(i, j + 1)) + + return m[i, j] + + return s(0, 0) diff --git a/longest-repeating-character-replacement/yolophg.py b/longest-repeating-character-replacement/yolophg.py new file mode 100644 index 000000000..39fac5844 --- /dev/null +++ b/longest-repeating-character-replacement/yolophg.py @@ -0,0 +1,27 @@ +# Time Complexity: O(n) - loop through the string once, and operations like `max(count.values())` are constant time because there are at most 26 characters. +# Space Complexity: O(1) - `count` only stores counts for up to 26 characters. + +class Solution: + def characterReplacement(self, s: str, k: int) -> int: + # keep track of counts in the current window + count = {} + + left, right = 0, 0 + res = 0 + + # move the right pointer across the string + for right in range(len(s)): + # update the count for the character at the right pointer + count[s[right]] = count.get(s[right], 0) + 1 + + # if the window size minus the most frequent char count is bigger than k, + # need to shrink the window from the left + while (right - left + 1) - max(count.values()) > k: + # reduce the count of the char at the left pointer and move the left pointer + count[s[left]] -= 1 + left += 1 + + # update the max length of the valid window + res = max(res, right - left + 1) + + return res diff --git a/number-of-1-bits/yolophg.py b/number-of-1-bits/yolophg.py new file mode 100644 index 000000000..0d84b17b6 --- /dev/null +++ b/number-of-1-bits/yolophg.py @@ -0,0 +1,15 @@ +# Time Complexity: O(k) - check each bit of n once. In the worst case, this is about 32 iterations. +# Space Complexity: O(1) - only use a constant amount of extra space. + +class Solution: + def hammingWeight(self, n: int) -> int: + count = 0 + + # keep going till n becomes 0 (no more bits left to check) + while n: + # check if the last bit is 1 (n % 2 tells us this) and add it to the count + count += (n % 2) + # shift n to the right by 1 to move to the next bit + n = n >> 1 + + return count diff --git a/sum-of-two-integers/yolophg.py b/sum-of-two-integers/yolophg.py new file mode 100644 index 000000000..25e8839ad --- /dev/null +++ b/sum-of-two-integers/yolophg.py @@ -0,0 +1,22 @@ +# Time Complexity: O(1) - fixed 32-bit operations, at most 32 iterations +# Space Complexity: O(1) - only uses a few integer variables + +class Solution: + def getSum(self, a: int, b: int) -> int: + # 32-bit mask to keep numbers within range + mask = 0xFFFFFFFF + # max value for a signed 32-bit integer (2^31 - 1) + MAX = 0x7FFFFFFF + + # keep going until there's no carry left + while b != 0: + # carry is AND operation, then shift left + carry = (a & b) << 1 + # XOR does the addition, keep it within 32 bits + a = (a ^ b) & mask + # carry becomes the new b (loop continues if carry exists) + b = carry & mask + + # if a is greater than MAX, it's actually a negative number in 32-bit terms + # convert it to proper negative representation + return a if a <= MAX else ~(a ^ mask)