From 5de975b9cf786fe9ba36c20b5067f8ca14451207 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 6 Jan 2019 14:59:33 -0800 Subject: [PATCH 001/344] 476, 485 --- 476 Number Complement.py | 27 +++++++++++++++++++++++++++ 485 Max Consecutive Ones.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 476 Number Complement.py create mode 100644 485 Max Consecutive Ones.py diff --git a/476 Number Complement.py b/476 Number Complement.py new file mode 100644 index 0000000..5e0db3f --- /dev/null +++ b/476 Number Complement.py @@ -0,0 +1,27 @@ +#!/usr/bin/python3 +""" +Given a positive integer, output its complement number. The complement strategy +is to flip the bits of its binary representation. + +Note: +The given integer is guaranteed to fit within the range of a 32-bit signed integer. +You could assume no leading zero bit in the integer’s binary representation. +""" + + +class Solution: + def findComplement(self, num): + """ + :type num: int + :rtype: int + """ + msb = 0 + while num >> msb: + msb += 1 + + mask = (1 << msb) - 1 + return mask & ~num + + +if __name__ == "__main__": + assert Solution().findComplement(5) == 2 diff --git a/485 Max Consecutive Ones.py b/485 Max Consecutive Ones.py new file mode 100644 index 0000000..21c2333 --- /dev/null +++ b/485 Max Consecutive Ones.py @@ -0,0 +1,31 @@ +#!/usr/bin/python3 +""" +Given a binary array, find the maximum number of consecutive 1s in this array. +""" + + +class Solution: + def findMaxConsecutiveOnes(self, nums): + """ + two pointers + :type nums: List[int] + :rtype: int + """ + s = 0 + e = 0 + ret = 0 + while s < len(nums): + if nums[s] == 1: + while e < len(nums) and nums[e] == 1: + e += 1 + ret = max(ret, e - s) + else: + e += 1 + + s = e + + return ret + + +if __name__ == "__main__": + assert Solution().findMaxConsecutiveOnes([1,1,0,1,1,1]) == 3 From ecdbaa9bad0647d43fe09aadbdc60d0efda4a82b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 6 Jan 2019 15:13:13 -0800 Subject: [PATCH 002/344] 500 --- 500 Keyboard Row.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 500 Keyboard Row.py diff --git a/500 Keyboard Row.py b/500 Keyboard Row.py new file mode 100644 index 0000000..2d25027 --- /dev/null +++ b/500 Keyboard Row.py @@ -0,0 +1,32 @@ +#!/usr/bin/python3 +""" +Given a List of words, return the words that can be typed using letters of +alphabet on only one row's of American keyboard like the image below. +""" + + +class Solution: + def findWords(self, words): + """ + :type words: List[str] + :rtype: List[str] + """ + rows = [ + "qwertyuiop", + "asdfghjkl", + "zxcvbnm", + ] + d = { + e: i + for i, v in enumerate(rows) + for e in v + } + return [ + w + for w in words + if all(d[w[0].lower()] == d[l.lower()] for l in w) + ] + + +if __name__ == "__main__": + assert Solution().findWords(["Hello", "Alaska", "Dad", "Peace"]) == ["Alaska", "Dad"] From d2b9793c755c7a0b36eb980b78226762fae7e33e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 16 Jan 2019 19:58:39 -0800 Subject: [PATCH 003/344] 413 --- 413 Arithmetic Slices.py | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 413 Arithmetic Slices.py diff --git a/413 Arithmetic Slices.py b/413 Arithmetic Slices.py new file mode 100644 index 0000000..c4af641 --- /dev/null +++ b/413 Arithmetic Slices.py @@ -0,0 +1,43 @@ +#!/usr/bin/python3 +""" +A sequence of number is called arithmetic if it consists of at least three +elements and if the difference between any two consecutive elements is the same. +""" + +class Solution: + def count(self, l): + return (l-1) * l // 2 + + def numberOfArithmeticSlices(self, A): + """ + Diff the array, find the pattern. + Find that it is a function of length of the sequence + With 3 consecutive sequence (l - 1) * l / 2 + + :type A: List[int] + :rtype: int + """ + ret = 0 + if len(A) < 3: + return ret + + delta = [] + for i in range(1, len(A)): + delta.append(A[i] - A[i-1]) + + s = 0 + e = 0 + while s < len(delta): + while e < len(delta) and delta[s] == delta[e]: + e += 1 + + l = e - s + ret += self.count(l) + + s = e + + return ret + + +if __name__ == "__main__": + assert Solution().numberOfArithmeticSlices([1, 2, 3, 4]) == 3 From 6329d17fe1c2283d59ebcf4a07551ce43d9c13de Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 16 Jan 2019 21:10:53 -0800 Subject: [PATCH 004/344] 446 --- 413 Arithmetic Slices.py | 4 +- 446 Arithmetic Slices II - Subsequence.py | 68 +++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 446 Arithmetic Slices II - Subsequence.py diff --git a/413 Arithmetic Slices.py b/413 Arithmetic Slices.py index c4af641..b4694db 100644 --- a/413 Arithmetic Slices.py +++ b/413 Arithmetic Slices.py @@ -2,6 +2,8 @@ """ A sequence of number is called arithmetic if it consists of at least three elements and if the difference between any two consecutive elements is the same. + +The function should return the number of arithmetic slices in the array A. """ class Solution: @@ -13,7 +15,7 @@ def numberOfArithmeticSlices(self, A): Diff the array, find the pattern. Find that it is a function of length of the sequence With 3 consecutive sequence (l - 1) * l / 2 - + :type A: List[int] :rtype: int """ diff --git a/446 Arithmetic Slices II - Subsequence.py b/446 Arithmetic Slices II - Subsequence.py new file mode 100644 index 0000000..e66e08f --- /dev/null +++ b/446 Arithmetic Slices II - Subsequence.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 +""" +A sequence of number is called arithmetic if it consists of at least three +elements and if the difference between any two consecutive elements is the same. + +The function should return the number of arithmetic subsequence slices in the +array A. (Subsequence rather than slide) +""" +from collections import defaultdict + + +class Solution: + def numberOfArithmeticSlices(self, A): + """ + Subsequence, count the number, looks like dp + use defaultdict for easy dp array construction + + D[i][d] stores the number of arithmetic subsequence ending at A[i], with + delta d + + result would be + sum( + D[i][d] + if >= 3 consecutive subsequence A[i], A[j], A[k] ... + for some j, k + ) + + summing D[j][d] rather than D[i][d] since we need >= 3 subsequence and + D[i][d] contains the length 2. + + This approach cannot be extended to >= 4 subsequence + :type A: List[int] + :rtype: int + """ + ret = 0 + D = defaultdict(lambda: defaultdict(int)) + for i in range(len(A)): + for j in range(i): + d = A[i] - A[j] + D[i][d] += 1 + D[j][d] + if D[j][d] > 0: + # >= 3 subsequence with A[k], A[j], A[i] + ret += D[j][d] # not D[i][d] + + return ret + + def numberOfArithmeticSlices_error(self, A): + """ + :type A: List[int] + :rtype: int + """ + ret = 0 + D = defaultdict(lambda: defaultdict(int)) + for i in range(len(A)): + for j in range(i): + delta = A[i] - A[j] + D[i][delta] += 1 + D[j][delta] + + for j in range(i): + delta = A[i] - A[j] + if D[j][delta] > 0: + ret += D[i][delta] # counted the length 2 + + return ret + + +if __name__ == "__main__": + assert Solution().numberOfArithmeticSlices([2, 4, 6, 8, 10]) == 7 From a3cd5318e1d9ea01c39ef2d6dbe377615ab38bac Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 16 Jan 2019 23:20:27 -0800 Subject: [PATCH 005/344] 416 --- 416 Partition Equal Subset Sum.py | 78 +++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 416 Partition Equal Subset Sum.py diff --git a/416 Partition Equal Subset Sum.py b/416 Partition Equal Subset Sum.py new file mode 100644 index 0000000..5b20bbc --- /dev/null +++ b/416 Partition Equal Subset Sum.py @@ -0,0 +1,78 @@ +#!/usr/bin/python3 +""" +Given a non-empty array containing only positive integers, find if the array can +be partitioned into two subsets such that the sum of elements in both subsets is +equal. +""" +from collections import defaultdict + + +class Solution: + def canPartition(self, nums): + """ + 0/1 Knapsack problem + + Carefully define the state: + Let d[i][s] be # subset of nums[:i+1], can be sum to s + + Transition function: + d[i][s] = d[i-1][s] + d[i-1][s-nums[i]] + = case not choose nums[i] + case choose nums[i] + + :type nums: List[int] + :rtype: bool + """ + if not nums: + return False + + s = sum(nums) + if s % 2 != 0: + return False + + target = s // 2 + d = defaultdict(lambda: defaultdict(int)) + d[0][0] = 1 + d[0][nums[0]] = 1 + + for i in range(1, len(nums)): + for v in range(target + 1): + d[i][v] = d[i-1][v] + d[i-1][v-nums[i]] + + return any(d[i][target] > 0 for i in range(len(nums))) + + def canPartition_TLE(self, nums): + """ + subset rather than sub array + positive number only + + dfs with pruning O(2^n), whether to choose the number or not + + :type nums: List[int] + :rtype: bool + """ + nums.sort() + s = sum(nums) + if s % 2 != 0: + return False + + target = s // 2 + return self.dfs(nums, 0, target) + + def dfs(self, nums, idx, target): + """Find a subset that sum to target""" + if not idx < len(nums): + return False + if nums[idx] == target: + return True + if nums[idx] > target: + return False + + return ( + self.dfs(nums, idx + 1, target) or # not take nums[idx] + self.dfs(nums, idx + 1, target - nums[idx]) # take nums[idx] + ) + + +if __name__ == "__main__": + assert Solution().canPartition([1, 5, 11, 5]) == True + assert Solution().canPartition([1, 2, 3, 5]) == False From 188ad5bec29e48493a7a4434e6661a238bc46cbd Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 19 Jan 2019 00:39:04 -0800 Subject: [PATCH 006/344] 417 starting point dfs --- 417 Pacific Atlantic Water Flow.py | 150 +++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 417 Pacific Atlantic Water Flow.py diff --git a/417 Pacific Atlantic Water Flow.py b/417 Pacific Atlantic Water Flow.py new file mode 100644 index 0000000..26ec85b --- /dev/null +++ b/417 Pacific Atlantic Water Flow.py @@ -0,0 +1,150 @@ +#!/usr/bin/python3 +""" +Given an m x n matrix of non-negative integers representing the height of each +nit cell in a continent, the "Pacific ocean" touches the left and top edges of +the matrix and the "Atlantic ocean" touches the right and bottom edges. + +Water can only flow in four directions (up, down, left, or right) from a cell to +another one with height equal or lower. + +Find the list of grid coordinates where water can flow to both the Pacific and +Atlantic ocean. + +Note: +The order of returned grid coordinates does not matter. +Both m and n are less than 150. +Example: + +Given the following 5x5 matrix: + + Pacific ~ ~ ~ ~ ~ + ~ 1 2 2 3 (5) * + ~ 3 2 3 (4) (4) * + ~ 2 4 (5) 3 1 * + ~ (6) (7) 1 4 5 * + ~ (5) 1 1 2 4 * + * * * * * Atlantic + +Return: + +[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (positions with +parentheses in above matrix). +""" +dirs = ((0, 1), (0, -1), (1, 0), (-1, 0)) + + +class Solution: + def pacificAtlantic(self, matrix): + """ + dfs, visisted O(1) + Similar to Trapping Rainwater II (BFS + heap), but no need to record + volume, thus, dfs is enough. + + Similar to longest increasing path + + Starting from the edge point rather than any point, dfs visit the + possible cell + + Complexity analysis, although a cell can be checked multiple times + (at most 4 times); but only perform 1 dfs on each cell; thus + O(mn) + + :type matrix: List[List[int]] + :rtype: List[List[int]] + """ + if not matrix or not matrix[0]: + return [] + + m, n = len(matrix), len(matrix[0]) # row, col + # don't do [[False] * n ] * m, memory management, all rows reference the same row + P = [[False for _ in range(n)] for _ in range(m)] + A = [[False for _ in range(n)] for _ in range(m)] + + # starting from edge point + for i in range(m): + self.dfs(matrix, i, 0, P) + self.dfs(matrix, i, n-1, A) + + for j in range(n): + self.dfs(matrix, 0, j, P) + self.dfs(matrix, m-1, j, A) + + ret = [ + [i, j] + for i in range(m) + for j in range(n) + if P[i][j] and A[i][j] + ] + return ret + + def dfs(self, matrix, i, j, C): + # check before dfs (to be consistent) + C[i][j] = True + m, n = len(matrix), len(matrix[0]) + for x, y in dirs: + I = i + x + J = j + y + if 0 <= I < m and 0 <= J < n and matrix[i][j] <= matrix[I][J]: + if not C[I][J]: + self.dfs(matrix, I, J, C) + + + def pacificAtlantic_error(self, matrix): + """ + DP + dfs, visisted O(1) + :type matrix: List[List[int]] + :rtype: List[List[int]] + """ + if not matrix or not matrix[0]: + return [] + + m, n = len(matrix), len(matrix[0]) # row, col + P = [[False] * n ] * m + A = [[False] * n ] * m + + visisted = [[False] * n ] * m + for i in range(m): + for j in range(n): + self.dfs_error(matrix, i, j, visisted, P, lambda i, j: i < 0 or j <0) + + visisted = [[False] * n ] * m + for i in range(m): + for j in range(n): + self.dfs_error(matrix, i, j, visisted, A, lambda i, j: i >= m or j >= n) + + ret = [ + [i, j] + for i in range(m) + for j in range(n) + if P[i][j] and A[i][j] + ] + return ret + + + def dfs_error(self, matrix, i, j, visisted, C, predicate): + m, n = len(matrix), len(matrix[0]) + if visisted[i][j]: + return C[i][j] + + visisted[i][j] = True + for x, y in dirs: + i2 = i + x + j2= j + y + if 0 <= i2 < m and 0 <= j2 < n: + if self.dfs_error(matrix, i2, j2, visisted, C, predicate) and matrix[i][j] >= matrix[i2][j2]: + C[i][j] = True + elif predicate(i2, j2): + C[i][j] = True + + return C[i][j] + + +if __name__ == "__main__": + assert Solution().pacificAtlantic([ + [1,2,2,3,5], + [3,2,3,4,4], + [2,4,5,3,1], + [6,7,1,4,5], + [5,1,1,2,4] + ]) == [[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] From 783d6c4370ee07e413d953c85dd15b7475fa7597 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 19 Jan 2019 23:01:30 -0800 Subject: [PATCH 007/344] 442 --- 421 Maximum XOR of Two Numbers in an Array.py | 38 ++++++++++++++ 442 Find All Duplicates in an Array.py | 50 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 421 Maximum XOR of Two Numbers in an Array.py create mode 100644 442 Find All Duplicates in an Array.py diff --git a/421 Maximum XOR of Two Numbers in an Array.py b/421 Maximum XOR of Two Numbers in an Array.py new file mode 100644 index 0000000..9a0829a --- /dev/null +++ b/421 Maximum XOR of Two Numbers in an Array.py @@ -0,0 +1,38 @@ +#!/usr/bin/python3 +""" +Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 2^31. + +Find the maximum result of ai XOR aj, where 0 ≤ i, j < n. + +Could you do this in O(n) runtime? +""" + + +class Solution: + def findMaximumXOR(self, nums): + """ + Brute force: O(n^2) + constrinat: 32 bit + check bit by bit rather than number by number + build the bit from MSB to LSB, since be need the largest + + :type nums: List[int] + :rtype: int + """ + ret = 0 + for i in reversed(range(32)): + prefixes = set(num >> i for num in nums) + ret <<= 1 + # fixing the remaining bit, set the LSB + cur = ret + 1 + for p in prefixes: + # a ^ b ^ a = b + if cur ^ p in prefixes: + ret = cur + break # found one + + return ret + + +if __name__ == "__main__": + assert Solution().findMaximumXOR([3, 10, 5, 25, 2, 8]) == 28 diff --git a/442 Find All Duplicates in an Array.py b/442 Find All Duplicates in an Array.py new file mode 100644 index 0000000..e2582bd --- /dev/null +++ b/442 Find All Duplicates in an Array.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +""" +Given an array of integers, 1 ≤ a[i] ≤ n (n = size of array), some elements +appear twice and others appear once. + +Find all the elements that appear twice in this array. + +Could you do it without extra space and in O(n) runtime? + +Example: +Input: +[4,3,2,7,8,2,3,1] + +Output: +[2,3] +""" + + +class Solution: + def idx(self, a): + return a - 1 + + def findDuplicates(self, A): + """ + Normally: hashmap + Without extra space + Extra constraint: 1 ≤ a[i] ≤ n + + :type A: List[int] + :rtype: List[int] + """ + for i in range(len(A)): + t = self.idx(A[i]) + while i != t: + if A[i] == A[t]: + break + else: + A[i], A[t] = A[t], A[i] + t = self.idx(A[i]) + + ret = [] + for i in range(len(A)): + if self.idx(A[i]) != i: + ret.append(A[i]) + + return ret + + +if __name__ == "__main__": + assert set(Solution().findDuplicates([4,3,2,7,8,2,3,1])) == set([2,3]) From 02a9d9f92091f110453c42495e63b42a5201ec1f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 20 Jan 2019 23:00:22 -0800 Subject: [PATCH 008/344] 424 --- ...Longest Repeating Character Replacement.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 424 Longest Repeating Character Replacement.py diff --git a/424 Longest Repeating Character Replacement.py b/424 Longest Repeating Character Replacement.py new file mode 100644 index 0000000..3deca2b --- /dev/null +++ b/424 Longest Repeating Character Replacement.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 +""" +Given a string that consists of only uppercase English letters, you can replace +any letter in the string with another letter at most k times. Find the length of +a longest substring containing all repeating letters you can get after +performing the above operations. +""" +import string +import operator + + +class Solution: + def characterReplacement(self, s, k): + """ + Replace any letter with another letter - replace any letter with any + letter. + + Longest substring with all repeating letters, replace k + + Sliding window, replace every letter except for the most common letter + to the most comm letter + + :type s: str + :type k: int + :rtype: int + """ + counter = { + alphabet: 0 + for alphabet in string.ascii_uppercase + } + lo = 0 + ret = 0 + assert k > 0 + for hi in range(len(s)): + counter[s[hi]] += 1 + while True: + most = max(counter.values()) # O(26) + l = hi - lo + 1 + if l - most > k: + counter[s[lo]] -= 1 + lo += 1 + else: + ret = max(ret, l) + break + + return ret + + +if __name__ == "__main__": + assert Solution().characterReplacement("AABABBA", 1) == 4 + assert Solution().characterReplacement("ABAB", 2) == 4 From 14a582b308af73fbc83eb33c3b67c2fbcad4a31f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 21 Jan 2019 19:39:59 -0800 Subject: [PATCH 009/344] 449 450 --- 449 Serialize and Deserialize BST.py | 88 ++++++++++++++++++++++ 450 Delete Node in a BST.py | 107 +++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 449 Serialize and Deserialize BST.py create mode 100644 450 Delete Node in a BST.py diff --git a/449 Serialize and Deserialize BST.py b/449 Serialize and Deserialize BST.py new file mode 100644 index 0000000..43d7a74 --- /dev/null +++ b/449 Serialize and Deserialize BST.py @@ -0,0 +1,88 @@ +""" +Serialization is the process of converting a data structure or object into a +sequence of bits so that it can be stored in a file or memory buffer, or +transmitted across a network connection link to be reconstructed later in the +same or another computer environment. + +Design an algorithm to serialize and deserialize a binary search tree. There is +no restriction on how your serialization/deserialization algorithm should work. +You just need to ensure that a binary search tree can be serialized to a string +and this string can be deserialized to the original tree structure. + +The encoded string should be as compact as possible. +""" + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Codec: + DELIMITER = "," + + def serialize(self, root): + """Encodes a tree to a single string. + Basic binary tree serialize (BFS), see Serialize and Deserialize + Binary Tree + + The main difference is as compact as possible. No need "null", since + insertion order is specfied. + + 3 (1) + 2 (2) 5 (2) + 6 (3) # bracket () is the insertion order + + pre-order traversal keeps the insertion order + :type root: TreeNode + :rtype: str + """ + def traverse(root, ret): + if not root: + return + + ret.append(root.val) + traverse(root.left, ret) + traverse(root.right, ret) + + ret = [] + traverse(root, ret) + return self.DELIMITER.join(map(str, ret)) + + def deserialize(self, data): + """Decodes your encoded data to tree. + + Normal BST insert + :type data: str + :rtype: TreeNode + """ + if not data: + return + + lst = list(map(int, data.split(self.DELIMITER))) + root = TreeNode(lst[0]) + def insert(root, val): + # need to keep the parent + if val < root.val: + if not root.left: + root.left = TreeNode(val) + else: + insert(root.left, val) + else: + if not root.right: + root.right = TreeNode(val) + else: + insert(root.right, val) + + for a in lst[1:]: + insert(root, a) + + return root + + +# Your Codec object will be instantiated and called as such: +# codec = Codec() +# codec.deserialize(codec.serialize(root)) diff --git a/450 Delete Node in a BST.py b/450 Delete Node in a BST.py new file mode 100644 index 0000000..a87345d --- /dev/null +++ b/450 Delete Node in a BST.py @@ -0,0 +1,107 @@ +#!/usr/bin/python3 +""" +Given a root node reference of a BST and a key, delete the node with the given +key in the BST. Return the root node reference (possibly updated) of the BST. + +Basically, the deletion can be divided into two stages: + +Search for a node to remove. +If the node is found, delete the node. + +Note: Time complexity should be O(height of tree). +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def deleteNode(self, root, key): + """ + :type root: TreeNode + :type key: int + :rtype: TreeNode + """ + return self._delete(root, key) + + def _delete(self, root, key): + """ + Pop the left max or right min + Return the root to keep the parent child relationship + """ + if not root: + return + + # check before recursion because need to know parent + if key < root.val: + root.left = self._delete(root.left, key) + return root + elif key > root.val: + root.right = self._delete(root.right, key) + return root + else: + if root.left: + maxa, left = self._pop_max(root.left) + root.left = left + root.val = maxa + return root + elif root.right: + mini, right = self._pop_min(root.right) + root.right = right + root.val = mini + return root + else: + return + + def _pop_max(self, root): + if root.right: + maxa, right = self._pop_max(root.right) + root.right = right + return maxa, root + # irrevelant with root.left, BST property + else: + return root.val, root.left + + def _pop_min(self, root): + if root.left: + mini, left = self._pop_min(root.left) + root.left = left + return mini, root + # irrevelant with root.right, BST property + else: + return root.val, root.right + + def _delete_error(self, root, key): + """ + need to know the parent, keep the reference + need to handle duplicate + + Error: need to find the max of the left subtree rather than the root + """ + if not root: + return + + # check before recursion because need to know parent + if key < root.val: + root.left = self._delete(root.left, key) + return root + elif key > root.val: + root.right = self._delete(root.right, key) + return root + else: + if root.left: + root.val = root.left.val + left = self._delete(root.left, root.left.val) + root.left = left + return root + elif root.right: + root.val = root.right.val + right = self._delete(root.right, root.right.val) + root.right = right + return root + else: + return From 1f78681ed86a2ac127d6437780e02505a44c58c0 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 21 Jan 2019 19:56:22 -0800 Subject: [PATCH 010/344] 421 --- 451 Sort Characters By Frequency.py | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 451 Sort Characters By Frequency.py diff --git a/451 Sort Characters By Frequency.py b/451 Sort Characters By Frequency.py new file mode 100644 index 0000000..8f71318 --- /dev/null +++ b/451 Sort Characters By Frequency.py @@ -0,0 +1,35 @@ +#!/usr/bin/python3 +""" +Given a string, sort it in decreasing order based on the frequency of characters. +""" +from collections import defaultdict + + +class Solution(object): + def frequencySort(self, s): + """ + Brute force: counter, sort O(n log n) + + There is a uppper limit of the counter, thus bucket sort possible + :type s: str + :rtype: str + """ + counter = defaultdict(int) + for c in s: + counter[c] += 1 + + bucket = {count: [] for count in range(1, len(s)+1)} + for k, v in counter.items(): + bucket[v].append(k) + + ret = [] + for count in reversed(range(1, len(s) + 1)): + if bucket[count]: + for c in bucket[count]: + ret.append(c * count) + + return "".join(ret) + + +if __name__ == "__main__": + assert Solution().frequencySort("tree") == "eetr" From 59bfe5b6c807e239d42342c88ed07179ecaa4b94 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 21 Jan 2019 20:20:44 -0800 Subject: [PATCH 011/344] 452 --- ...imum Number of Arrows to Burst Balloons.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 452 Minimum Number of Arrows to Burst Balloons.py diff --git a/452 Minimum Number of Arrows to Burst Balloons.py b/452 Minimum Number of Arrows to Burst Balloons.py new file mode 100644 index 0000000..fee91d0 --- /dev/null +++ b/452 Minimum Number of Arrows to Burst Balloons.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +""" +There are a number of spherical balloons spread in two-dimensional space. For +each balloon, provided input is the start and end coordinates of the horizontal +diameter. Since it's horizontal, y-coordinates don't matter and hence the +x-coordinates of start and end of the diameter suffice. Start is always smaller +than end. There will be at most 104 balloons. + +An arrow can be shot up exactly vertically from different points along the +x-axis. A balloon with xstart and xend bursts by an arrow shot at x if +x_start ≤ x ≤ x_end. There is no limit to the number of arrows that can be shot. +An arrow once shot keeps travelling up infinitely. The problem is to find the +minimum number of arrows that must be shot to burst all balloons. + +Example: + +Input: +[[10,16], [2,8], [1,6], [7,12]] + +Output: +2 + +Explanation: +One way is to shoot one arrow for example at x = 6 (bursting the balloons [2,8] +and [1,6]) and another arrow at x = 11 (bursting the other two balloons). +""" +import heapq + + +class Balloon: + def __init__(self, s, e): + self.s = s + self.e = e + + def __lt__(self, other): + # __cmp__ removed in py3 + return self.e < other.e + + +class Solution: + def findMinArrowShots(self, points): + """ + greedy shot since if two balloon no overlap, then must shot separately + + heap: min, insert by s, pop by e + Like the maximum overlapping interval + + :type points: List[List[int]] + :rtype: int + """ + ret = 0 + points.sort(key=lambda x: x[0]) + heap = [] + for point in points: + s, e = point + if heap and heap[0].e < s: + ret += 1 + heap = [] + + heapq.heappush(heap, Balloon(s, e)) + + if heap: + ret += 1 + + return ret + + +if __name__ == "__main__": + assert Solution().findMinArrowShots([[10,16], [2,8], [1,6], [7,12]]) == 2 From 8327297430cdb0b3274bf423e5e6ed8d2ef2f27f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 21 Jan 2019 20:40:56 -0800 Subject: [PATCH 012/344] 454 --- 454 4Sum II.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 454 4Sum II.py diff --git a/454 4Sum II.py b/454 4Sum II.py new file mode 100644 index 0000000..501c069 --- /dev/null +++ b/454 4Sum II.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +Given four lists A, B, C, D of integer values, compute how many tuples (i, j, +k, l) there are such that A[i] + B[j] + C[k] + D[l] is zero. + +To make problem a bit easier, all A, B, C, D have same length of N where +0 ≤ N ≤ 500. All integers are in the range of -2^28 to 2^28 - 1 and the result +is guaranteed to be at most 2^31 - 1. + +Example: + +Input: +A = [ 1, 2] +B = [-2,-1] +C = [-1, 2] +D = [ 0, 2] + +Output: +2 + +Explanation: +The two tuples are: +1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0 +2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0 +""" +from collections import defaultdict + + +class Solution: + def fourSumCount(self, A, B, C, D): + """ + Brute force with map: O(N^3) + + O(N^3) is pretty large, O(N^2) or O(N log N)? + + O(N^2) to sum cartesian product (A, B) to construct index + similar to C, D. + + Then index loop up + :type A: List[int] + :type B: List[int] + :type C: List[int] + :type D: List[int] + :rtype: int + """ + N = len(A) + AB = defaultdict(int) + CD = defaultdict(int) + for i in range(N): + for j in range(N): + AB[A[i] + B[j]] += 1 + CD[C[i] + D[j]] += 1 + + ret = 0 + for gross, count in AB.items(): + target = 0 - gross + ret += count * CD[target] + + return ret + + +if __name__ == "__main__": + A = [ 1, 2] + B = [-2,-1] + C = [-1, 2] + D = [ 0, 2] + assert Solution().fourSumCount(A, B, C, D) == 2 From fa1d58a701cb3e696d1e7e72fcbff5535dbf2ca1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 21 Jan 2019 21:55:05 -0800 Subject: [PATCH 013/344] 462 --- 454 4Sum II.py | 1 + ...inimum Moves to Equal Array Elements II.py | 85 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 462 Minimum Moves to Equal Array Elements II.py diff --git a/454 4Sum II.py b/454 4Sum II.py index 501c069..307190f 100644 --- a/454 4Sum II.py +++ b/454 4Sum II.py @@ -52,6 +52,7 @@ def fourSumCount(self, A, B, C, D): CD[C[i] + D[j]] += 1 ret = 0 + # O(N^2) for gross, count in AB.items(): target = 0 - gross ret += count * CD[target] diff --git a/462 Minimum Moves to Equal Array Elements II.py b/462 Minimum Moves to Equal Array Elements II.py new file mode 100644 index 0000000..5218159 --- /dev/null +++ b/462 Minimum Moves to Equal Array Elements II.py @@ -0,0 +1,85 @@ +#!/usr/bin/python3 +""" +Given a non-empty integer array, find the minimum number of moves required to +make all array elements equal, where a move is incrementing a selected element +by 1 or decrementing a selected element by 1. + +You may assume the array's length is at most 10,000. + +Example: + +Input: +[1,2,3] + +Output: +2 + +Explanation: +Only two moves are needed (remember each move increments or decrements one +element): + +[1,2,3] => [2,2,3] => [2,2,2] +""" + + +class Solution: + def pivot(self, A, lo, hi): + pivot = lo + closed = pivot # closed == pivot, means no closed set + for i in range(lo + 1, hi): + if A[i] < A[pivot]: + closed += 1 + A[closed], A[i] = A[i], A[closed] + + A[closed], A[pivot] = A[pivot], A[closed] + return closed # the pivot index + + def quick_select(self, nums, lo, hi, k): + """find k-th (0-indexed)""" + pivot = self.pivot(nums, lo, hi) + if pivot == k: + return nums[pivot] + elif pivot > k: + return self.quick_select(nums, lo, pivot, k) + else: + return self.quick_select(nums, pivot + 1, hi, k) + + + def minMoves2(self, nums): + """ + find the median rather than the average + + No matter which middle point you pick, the total running length for min + and max is the same. |-------|-----| + + So, we can effectively reduce the problem size from n to n-2 by + discarding min and max points. + :type nums: List[int] + :rtype: int + """ + n = len(nums) + median = self.quick_select(nums, 0, n, n//2) + return sum(map(lambda x: abs(x - median), nums)) + + def find_median(self, nums): + n = len(nums) + nums.sort() + return nums[n//2] + + def minMoves2_error(self, nums): + """ + move to the average, since incr and decr cost is 1 + + error at [1, 0, 0, 8, 6] + + :type nums: List[int] + :rtype: int + """ + n = len(nums) + avg = round(sum(nums) / n) + return sum(map(lambda x: abs(x - avg), nums)) + + +if __name__ == "__main__": + assert Solution().minMoves2([1,2,3]) == 2 + assert Solution().minMoves2([1,0,0,8,6]) == 14 From d8adca47f9b92164c833664e974953487990083a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 21 Jan 2019 23:43:40 -0800 Subject: [PATCH 014/344] 464 --- 464 Can I Win.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 464 Can I Win.py diff --git a/464 Can I Win.py b/464 Can I Win.py new file mode 100644 index 0000000..6f612ee --- /dev/null +++ b/464 Can I Win.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +In the "100 game," two players take turns adding, to a running total, any +integer from 1..10. The player who first causes the running total to reach or +exceed 100 wins. + +What if we change the game so that players cannot re-use integers? + +For example, two players might take turns drawing from a common pool of numbers +of 1..15 without replacement until they reach a total >= 100. + +Given an integer maxChoosableInteger and another integer desiredTotal, determine +if the first player to move can force a win, assuming both players play +optimally. + +You can always assume that maxChoosableInteger will not be larger than 20 and +desiredTotal will not be larger than 300. +""" + + +class Solution: + def canIWin(self, maxChoosableInteger, desiredTotal): + """ + can p win? + F^p_{total, choice_set - i} = not any( + F^p'_{total - A[j] - A[i], choice_set - j - i} + for j + ) + + :type maxChoosableInteger: int + :type desiredTotal: int + :rtype: bool + """ + cache = {} + # set is not hashable while frozenset is + choices = frozenset([choice for choice in range(1, maxChoosableInteger + 1)]) + return self._can_win(desiredTotal, choices, sum(choices), cache) + + def _can_win(self, total, choices, gross,cache): + if (total, choices) in cache: + return cache[(total, choices)] + + ret = False + if max(choices) >= total: + ret = True + + elif gross < total: + ret = False + else: + for choice in choices: + if not self._can_win( + total - choice, + choices - set([choice]), + gross - choice, + cache + ): + ret = True + break + + cache[(total, choices)] = ret + return ret + + +if __name__ == "__main__": + assert Solution().canIWin(10, 11) == False + assert Solution().canIWin(10, 0) == True + assert Solution().canIWin(13, 11) == True From 52e3092d91e6aa9c417fb7a42c06bf10f3cea4ad Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 23 Jan 2019 23:27:46 -0800 Subject: [PATCH 015/344] 435 --- 435 Non-overlapping Intervals.py | 52 ++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 435 Non-overlapping Intervals.py diff --git a/435 Non-overlapping Intervals.py b/435 Non-overlapping Intervals.py new file mode 100644 index 0000000..48eb45b --- /dev/null +++ b/435 Non-overlapping Intervals.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 +""" +Given a collection of intervals, find the minimum number of intervals you need +to remove to make the rest of the intervals non-overlapping. + +Note: +You may assume the interval's end point is always bigger than its start point. +Intervals like [1,2] and [2,3] have borders "touching" but they don't overlap +each other. +""" + +# Definition for an interval. +class Interval: + def __init__(self, s=0, e=0): + self.start = s + self.end = e + + @classmethod + def new(cls, lst): + return [ + cls(s, e) + for s, e in lst + ] + + +class Solution: + def eraseOverlapIntervals(self, intervals): + """ + Greedy remove the large e when overlapping + :type intervals: List[Interval] + :rtype: int + """ + ret = 0 + if not intervals: + return ret + + intervals.sort(key=lambda x: x.start) + cur = intervals[0] + for itv in intervals[1:]: + if cur.end <= itv.start: + cur = itv + else: + ret += 1 + cur = cur if cur.end < itv.end else itv + + return ret + + +if __name__ == "__main__": + assert Solution().eraseOverlapIntervals(Interval.new([ [1,2], [2,3], [3,4], [1,3] ])) == 1 + assert Solution().eraseOverlapIntervals(Interval.new([ [1,2], [1,2], [1,2] ])) == 2 + assert Solution().eraseOverlapIntervals(Interval.new([ [1,2], [2,3] ])) == 0 From e814a02fbff20cffb2e04f1b1d837d900c15576f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 23 Jan 2019 23:54:12 -0800 Subject: [PATCH 016/344] 436 --- 436 Find Right Interval.py | 61 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 436 Find Right Interval.py diff --git a/436 Find Right Interval.py b/436 Find Right Interval.py new file mode 100644 index 0000000..5145e30 --- /dev/null +++ b/436 Find Right Interval.py @@ -0,0 +1,61 @@ +#!/usr/bin/python3 +""" +Given a set of intervals, for each of the interval i, check if there exists an +interval j whose start point is bigger than or equal to the end point of the +interval i, which can be called that j is on the "right" of i. + +For any interval i, you need to store the minimum interval j's index, which +means that the interval j has the minimum start point to build the "right" +relationship for interval i. If the interval j doesn't exist, store -1 for the +interval i. Finally, you need output the stored value of each interval as an +array. + +Note: +You may assume the interval's end point is always bigger than its start point. +You may assume none of these intervals have the same start point. +""" +class Interval: + def __init__(self, s=0, e=0): + self.start = s + self.end = e + + @classmethod + def new(cls, lst): + return [ + cls(s, e) + for s, e in lst + ] + +from bisect import bisect_left + + +class Solution: + def findRightInterval(self, intervals): + """ + given e, find the right s - bisect + + :type intervals: List[Interval] + :rtype: List[int] + """ + indexes = { + itv.start: idx + for idx, itv in enumerate(intervals) + } + starts = list(sorted(indexes.keys())) + ret = [] + for itv in intervals: + idx = bisect_left(starts, itv.end) + if idx >= len(starts): + ret.append(-1) + else: + ret.append( + indexes[starts[idx]] + ) + + return ret + + +if __name__ == "__main__": + assert Solution().findRightInterval(Interval.new([ [3,4], [2,3], [1,2] ])) == [-1, 0, 1] + assert Solution().findRightInterval(Interval.new([ [1,2] ])) == [-1] + assert Solution().findRightInterval(Interval.new([ [1,4], [2,3], [3,4] ])) == [-1, 2, -1] From 462925094b8b7a68570053c127b8cf1e656923e3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 24 Jan 2019 21:54:13 -0800 Subject: [PATCH 017/344] 456 --- 456 132 Pattern.py | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 456 132 Pattern.py diff --git a/456 132 Pattern.py b/456 132 Pattern.py new file mode 100644 index 0000000..842d52f --- /dev/null +++ b/456 132 Pattern.py @@ -0,0 +1,72 @@ +#!/usr/bin/python3 +""" +Given a sequence of n integers a1, a2, ..., an, a 132 pattern is a subsequence +ai, aj, ak such that i < j < k and ai < ak < aj. Design an algorithm that takes +a list of n numbers as input and checks whether there is a 132 pattern in the +list. + +Note: n will be less than 15,000. +""" + + +class Solution: + def find132pattern(self, nums): + """ + Brute force i, j, k O(n^3) + + Optimize: I only need to keep the max number for the middle. O(N^2) + + Need better optimization? + I need to keep both the min and max number for A[:i] when scanning A[i] + min must be at left of max + + When scanning A[i], we need a list to keep both the min and max interval + [min, max]. The list maintains intervals that end at A[i-1] and start at + the min(A[:i-1]) + 0. The max is not increasing, thus can only keep A[i-1] + 1. F(i) = min(A[:i-1]) is strictly non-increasing (softly decreasing) + 2. We don't need to keep any internal that interval.end <= A[i] + 3. The list can be replaced with stack + + O(N) since every number enters and pops the stack once + + :type nums: List[int] + :rtype: bool + """ + stack = [] # List[Interval] + mini = float('Inf') + for v in nums: + while stack and stack[-1][1] <= v: # error when < (e.g. [-2, 1, 1]) + stack.pop() + if stack and stack[-1][0] < v: + return True + mini = min(mini, v) + stack.append((mini, v)) + + return False + + + def find132pattern_TLE(self, nums): + """ + Brute force i, j, k O(n^3) + + Optimize: you only need to keep the max number for the middle. O(N^2) + :type nums: List[int] + :rtype: bool + """ + for i in range(len(nums)): + maxa = nums[i] + for j in range(i + 1, len(nums)): + if nums[j] > nums[i]: + if nums[j] < maxa: + return True + maxa = max(maxa, nums[j]) + + return False + + +if __name__ == "__main__": + assert Solution().find132pattern([1, 2, 3, 4]) == False + assert Solution().find132pattern([3, 1, 4, 2]) == True + assert Solution().find132pattern([-1, 3, 2, 0]) == True + assert Solution().find132pattern([-2, 1, 1]) == True From 5165009dc14e5e290af8cb5ae2bd5e3aeb8c5482 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 24 Jan 2019 22:05:32 -0800 Subject: [PATCH 018/344] 470 --- 470 Implement Rand10() Using Rand7().py | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 470 Implement Rand10() Using Rand7().py diff --git a/470 Implement Rand10() Using Rand7().py b/470 Implement Rand10() Using Rand7().py new file mode 100644 index 0000000..9b2f8fc --- /dev/null +++ b/470 Implement Rand10() Using Rand7().py @@ -0,0 +1,30 @@ +#!/usr/bin/python3 +""" +Given a function rand7 which generates a uniform random integer in the range 1 +to 7, write a function rand10 which generates a uniform random integer in the +range 1 to 10. + +Do NOT use system's Math.random(). +""" + + +# The rand7() API is already defined for you. +def rand7(): + return 0 + + +class Solution: + def rand10(self): + """ + generate 7 twice, (rv1, rv2), 49 combination + assign 40 combinations for the 1 to 10 respectively + + 7-ary system + :rtype: int + """ + while True: + rv1 = rand7() + rv2 = rand7() + s = (rv1 - 1) * 7 + (rv2 - 1) # make it start from 0 + if s < 40: # s \in [0, 40) + return s % 10 + 1 # since I make it start from 0 From c8a0f4616147d03d47bbf30192231bb03fb0cfe1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 24 Jan 2019 23:02:25 -0800 Subject: [PATCH 019/344] 467 --- 467 Unique Substrings in Wraparound String.py | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 467 Unique Substrings in Wraparound String.py diff --git a/467 Unique Substrings in Wraparound String.py b/467 Unique Substrings in Wraparound String.py new file mode 100644 index 0000000..537ef02 --- /dev/null +++ b/467 Unique Substrings in Wraparound String.py @@ -0,0 +1,93 @@ +#!/usr/bin/python3 +""" +Consider the string s to be the infinite wraparound string of +"abcdefghijklmnopqrstuvwxyz", so s will look like this: +"...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd....". + +Now we have another string p. Your job is to find out how many unique non-empty +substrings of p are present in s. In particular, your input is the string p and +you need to output the number of different non-empty substrings of p in the +string s. + +Note: p consists of only lowercase English letters and the size of p might be +over 10000. + +Example 1: +Input: "a" +Output: 1 + +Explanation: Only the substring "a" of string "a" is in the string s. +Example 2: +Input: "cac" +Output: 2 +Explanation: There are two substrings "a", "c" of string "cac" in the string s. +Example 3: +Input: "zab" +Output: 6 +Explanation: There are six substrings "z", "a", "b", "za", "ab", "zab" of string +"zab" in the string s. +""" + + +class Solution: + def findSubstringInWraproundString(self, p): + """ + wrap around: +1 (delta=1) + "zab": 3 + 2 + 1 + "zabc": 4 + 3 + 2 + 1 + To de-dpulicate, change the way of counting - count backward at the + ending char. + "zabc": + "z": "z" : 1 + "a": "a", "za": 2 + "zab": "b", "ab", "zab": 3 + "zabc": "c", ...: 4 + + p.s. possible to count forward but tedious + :type p: str + :rtype: int + """ + counter = { + c: 1 + for c in p + } + l = 1 + for i in range(1, len(p)): + if (ord(p[i]) - ord(p[i-1])) % 26 == 1: # (0 - 25) % 26 == 1 + l += 1 + else: + l = 1 + counter[p[i]] = max(counter[p[i]], l) + + return sum(counter.values()) + + def findSubstringInWraproundString_error(self, p): + """ + wrap around: +1 (delta=1) + "zab": 3 + 2 + 1 + "zabc": 4 + 3 + 2 + 1 + :type p: str + :rtype: int + """ + if not p: + return 0 + + ret = set() + i = 0 + while i < len(p): + cur = [p[i]] + j = i + 1 + while j < len(p) and (ord(p[j]) - ord(cur[-1]) == 1 or p[j] == "a" and cur[-1] == "z"): + cur.append(p[j]) + j += 1 + ret.add("".join(cur)) + i = j + + return sum(map(lambda x: (len(x) + 1) * len(x) // 2, ret)) + + +if __name__ == "__main__": + assert Solution().findSubstringInWraproundString("a") == 1 + assert Solution().findSubstringInWraproundString("cac") == 2 + assert Solution().findSubstringInWraproundString("zab") == 6 + assert Solution().findSubstringInWraproundString("zaba") == 6 From e1683c0b16e484aea697677152ef6221c852adff Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 26 Jan 2019 21:46:03 -0800 Subject: [PATCH 020/344] 433 --- 127 Word Ladder.py | 40 +++++++++++++++++-- 433 Minimum Genetic Mutation.py | 70 +++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 433 Minimum Genetic Mutation.py diff --git a/127 Word Ladder.py b/127 Word Ladder.py index 4e7962d..0dbaf7b 100644 --- a/127 Word Ladder.py +++ b/127 Word Ladder.py @@ -19,7 +19,40 @@ All words contain only lowercase alphabetic characters. """ __author__ = 'Danyang' + + class Solution: + def is_neighbor(self, p, q): + diff = 0 + for a, b in zip(p, q): + if a != b: + diff += 1 + if diff > 1: + return False + return True + + def ladderLength(self, start, end, dct): + """ + bfs + """ + q = [start] + visited = {start} + lvl = 1 + while q: + cur_q = [] + for a in q: + if a == end: + return lvl + for b in dct: + if b not in visited and self.is_neighbor(a, b): + visited.add(b) + cur_q.append(b) + + lvl += 1 + q = cur_q + + return 0 + def ladderLength_TLE(self, start, end, dict): """ bfs @@ -115,7 +148,7 @@ def diff_count(str1, str2): return path_len - def ladderLength(self, start, end, dict): + def ladderLength_complex(self, start, end, dict): """ bfs @@ -160,7 +193,7 @@ def ladderLength(self, start, end, dict): if __name__=="__main__": - print Solution().ladderLength("sand", "acne", set( + assert Solution().ladderLength("sand", "acne", set( ["slit", "bunk", "wars", "ping", "viva", "wynn", "wows", "irks", "gang", "pool", "mock", "fort", "heel", "send", "ship", "cols", "alec", "foal", "nabs", "gaze", "giza", "mays", "dogs", "karo", "cums", "jedi", "webb", "lend", "mire", "jose", "catt", "grow", "toss", "magi", "leis", "bead", "kara", "hoof", "than", "ires", "baas", "vein", @@ -364,4 +397,5 @@ def ladderLength(self, start, end, dict): "oral", "gets", "chid", "yens", "snub", "ages", "wide", "bail", "verb", "lamb", "bomb", "army", "yoke", "gels", "tits", "bork", "mils", "nary", "barn", "hype", "odom", "avon", "hewn", "rios", "cams", "tact", "boss", "oleo", "duke", "eris", "gwen", "elms", "deon", "sims", "quit", "nest", "font", "dues", "yeas", "zeta", "bevy", "gent", - "torn", "cups", "worm", "baum", "axon", "purr", "vise", "grew", "govs", "meat", "chef", "rest", "lame"])) \ No newline at end of file + "torn", "cups", "worm", "baum", "axon", "purr", "vise", "grew", "govs", "meat", "chef", "rest", "lame"]) + ) == 11 diff --git a/433 Minimum Genetic Mutation.py b/433 Minimum Genetic Mutation.py new file mode 100644 index 0000000..d408ae1 --- /dev/null +++ b/433 Minimum Genetic Mutation.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +""" +A gene string can be represented by an 8-character long string, with choices +from "A", "C", "G", "T". + +Suppose we need to investigate about a mutation (mutation from "start" to +"end"), where ONE mutation is defined as ONE single character changed in the +gene string. + +For example, "AACCGGTT" -> "AACCGGTA" is 1 mutation. + +Also, there is a given gene "bank", which records all the valid gene mutations. +A gene must be in the bank to make it a valid gene string. + +Now, given 3 things - start, end, bank, your task is to determine what is the +minimum number of mutations needed to mutate from "start" to "end". If there is +no such a mutation, return -1. + +Note: + +Starting point is assumed to be valid, so it might not be included in the bank. +If multiple mutations are needed, all mutations during in the sequence must be +valid. +You may assume start and end string is not the same. +""" + + +class Solution: + def is_neighbor(self, p, q): + diff = 0 + for a, b in zip(p, q): + if a != b: + diff += 1 + if diff > 1: + return False + return True + + def minMutation(self, start, end, bank): + """ + BFS, record level and avoid loop + + Similar to 127 Word Ladder + + :type start: str + :type end: str + :type bank: List[str] + :rtype: int + """ + q = [start] + visited = {start} + lvl = 0 + while q: + cur_q = [] + for e in q: + if e == end: + return lvl + for t in bank: + if t not in visited and self.is_neighbor(e, t): + visited.add(t) + cur_q.append(t) + + lvl += 1 + q = cur_q + + return -1 + + +if __name__ == "__main__": + assert Solution().minMutation("AACCTTGG", "AATTCCGG", ["AATTCCGG","AACCTGGG","AACCCCGG","AACCTACC"]) == -1 + assert Solution().minMutation("AACCGGTT", "AAACGGTA", ["AACCGGTA", "AACCGCTA", "AAACGGTA"]) == 2 From 0e7dc19e7c2d7f14dad9483c6ec90455bf093899 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 26 Jan 2019 22:08:29 -0800 Subject: [PATCH 021/344] 473 --- 473 Matchsticks to Square.py | 62 ++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 473 Matchsticks to Square.py diff --git a/473 Matchsticks to Square.py b/473 Matchsticks to Square.py new file mode 100644 index 0000000..83f2fa7 --- /dev/null +++ b/473 Matchsticks to Square.py @@ -0,0 +1,62 @@ +#!/usr/bin/python3 +""" +Remember the story of Little Match Girl? By now, you know exactly what +matchsticks the little match girl has, please find out a way you can make one +square by using up all those matchsticks. You should not break any stick, but +you can link them up, and each matchstick must be used exactly one time. + +Your input will be several matchsticks the girl has, represented with their +stick length. Your output will either be true or false, to represent whether +you could make one square using all the matchsticks the little match girl has. + +Example 1: +Input: [1,1,2,2,2] +Output: true + +Explanation: You can form a square with length 2, one side of the square came +two sticks with length 1. +Example 2: +Input: [3,3,3,3,4] +Output: false + +Explanation: You cannot find a way to form a square with all the matchsticks. +""" + + +class Solution: + def makesquare(self, nums): + """ + need to use up all the stics + greedily fit the largest first + + :type nums: List[int] + :rtype: bool + """ + if not nums: + return False + + square = [0 for _ in range(4)] + l = sum(nums) // 4 + if sum(nums) % 4 != 0: + return False + + nums.sort(reverse=True) + return self.dfs(nums, 0, l, square) + + def dfs(self, nums, i, l, square): + if i >= len(nums): + return True + + for j in range(len(square)): + if nums[i] + square[j] <= l: + square[j] += nums[i] + if self.dfs(nums, i + 1, l, square): + return True + square[j] -= nums[i] + + return False + + +if __name__ == "__main__": + assert Solution().makesquare([1,1,2,2,2]) == True + assert Solution().makesquare([3,3,3,3,4]) == False From aa7be7439cf36bc00b72a9516750ba3f572c64ef Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 27 Jan 2019 01:08:46 -0800 Subject: [PATCH 022/344] 474 --- 473 Matchsticks to Square.py | 5 +- 474 Ones and Zeroes.py | 177 +++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 474 Ones and Zeroes.py diff --git a/473 Matchsticks to Square.py b/473 Matchsticks to Square.py index 83f2fa7..792c5d5 100644 --- a/473 Matchsticks to Square.py +++ b/473 Matchsticks to Square.py @@ -27,14 +27,15 @@ class Solution: def makesquare(self, nums): """ need to use up all the stics - greedily fit the largest first + greedily fit the largest first - error, consider [5, 4, 2, 2, 2, 2, 3] + need to dfs :type nums: List[int] :rtype: bool """ if not nums: return False - + square = [0 for _ in range(4)] l = sum(nums) // 4 if sum(nums) % 4 != 0: diff --git a/474 Ones and Zeroes.py b/474 Ones and Zeroes.py new file mode 100644 index 0000000..a15a4bf --- /dev/null +++ b/474 Ones and Zeroes.py @@ -0,0 +1,177 @@ +#!/usr/bin/python3 +""" +In the computer world, use restricted resource you have to generate maximum +benefit is what we always want to pursue. + +For now, suppose you are a dominator of m 0s and n 1s respectively. On the other +hand, there is an array with strings consisting of only 0s and 1s. + +Now your task is to find the maximum number of strings that you can form with +given m 0s and n 1s. Each 0 and 1 can be used at most once. + +Note: +The given numbers of 0s and 1s will both not exceed 100 +The size of given string array won't exceed 600. +Example 1: +Input: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3 +Output: 4 + +Explanation: This are totally 4 strings can be formed by the using of 5 0s and +3 1s, which are “10,”0001”,”1”,”0” +Example 2: +Input: Array = {"10", "0", "1"}, m = 1, n = 1 +Output: 2 + +Explanation: You could form "10", but then you'd have nothing left. Better form +"0" and "1". +""" +from collections import Counter + + +class Solution: + def findMaxForm(self, strs, m, n): + """ + 0-1 knapsack + let F[p][q][i] be the max end at A[i], with p 0's and q 1's remaining + F[p][q][i] = max(F[p'][q'][i-1] + 1, F[p][q][i-1]) + + To save space, drop i + + :type strs: List[str] + :type m: int + :type n: int + :rtype: int + """ + if not strs: + return 0 + + F = [[0 for _ in range(n + 1)] for _ in range(m + 1)] + z, o = self.count(strs[0]) + for i in range(m+1): + for j in range(n+1): + if i + z<= m and j + o <= n: + F[i][j] = 1 + + for e in range(1, len(strs)): + z, o = self.count(strs[e]) + for i in range(m+1): + for j in range(n+1): + if i + z <= m and j + o <= n: + F[i][j] = max( + F[i][j], + F[i + z][j + o] + 1 + ) + + ret = max( + F[i][j] + for i in range(m + 1) + for j in range(n + 1) + ) + return ret + + def count(self, s): + z, o = 0, 0 + for e in s: + if e == "0": + z += 1 + else: + o += 1 + + return z, o + + def findMaxForm_TLE(self, strs, m, n): + """ + 0-1 knapsack + let F[p][q][i] be the max end at A[i], with p 0's and q 1's + F[p][q][i] = max(F[p'][q'][i-1] + 1, F[p][q][i-1]) + + :type strs: List[str] + :type m: int + :type n: int + :rtype: int + """ + if not strs: + return 0 + + F = [[[0 for _ in range(len(strs))] for _ in range(n + 1)] for _ in range(m + 1)] + count = Counter(strs[0]) + for i in range(m+1): + for j in range(n+1): + if i + count["0"] <= m and j + count["1"] <= n: + F[i][j][0] = 1 + + for e in range(1, len(strs)): + count = Counter(strs[e]) + for i in range(m+1): + for j in range(n+1): + if i + count["0"] <= m and j + count["1"] <= n: + F[i][j][e] = F[i + count["0"]][j + count["1"]][e-1] + 1 + F[i][j][e] = max(F[i][j][e], F[i][j][e-1]) + + ret = max( + F[i][j][-1] + for i in range(m + 1) + for j in range(n + 1) + ) + return ret + + def findMaxForm_error(self, strs, m, n): + """ + 0-1 knapsack + let F[p][q][i] be the max end at A[i], with p 0's and q 1's + F[p][q][i] = max(F[p'][q'][i-1] + 1, F[p][q][i-1]) + + :type strs: List[str] + :type m: int + :type n: int + :rtype: int + """ + if not strs: + return 0 + + F = [[[0 for _ in range(len(strs))] for _ in range(n + 1)] for _ in range(m + 1)] + count = Counter(strs[0]) + if count["0"] <= m and count["1"] <= n: + F[m - count["0"]][n - count["1"]][0] += 1 + + for e in range(1, len(strs)): + count = Counter(strs[e]) + for i in range(m+1): + for j in range(n+1): + if count["0"] <= i and count["1"] <= j: + F[i - count["0"]][j - count["1"]][e] = F[i][j][e-1] + 1 + else: + F[i][j][e] = F[i][j][e-1] + + ret = max( + F[i][j][-1] + for i in range(m + 1) + for j in range(n + 1) + ) + return ret + + def findMaxForm_error(self, strs, m, n): + """ + reward is 1 regarless of length, then greedy - error + + :type strs: List[str] + :type m: int + :type n: int + :rtype: int + """ + strs.sort(key=len) + ret = 0 + for a in strs: + count = Counter(a) + if count["0"] <= m and count["1"] <= n: + ret += 1 + m -= count["0"] + n -= count["1"] + + return ret + + +if __name__ == "__main__": + assert Solution().findMaxForm(["10", "0001", "111001", "1", "0"], 5, 3) == 4 + assert Solution().findMaxForm(["10", "0", "1"], 1, 1) == 2 + assert Solution().findMaxForm(["111", "1000", "1000", "1000"], 9, 3) == 3 From c97377c80bd2ee7cc8b17c1554c120467cbd6bdc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 27 Jan 2019 01:39:06 -0800 Subject: [PATCH 023/344] 477 --- 477 Total Hamming Distance.py | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 477 Total Hamming Distance.py diff --git a/477 Total Hamming Distance.py b/477 Total Hamming Distance.py new file mode 100644 index 0000000..371938e --- /dev/null +++ b/477 Total Hamming Distance.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +""" +The Hamming distance between two integers is the number of positions at which +the corresponding bits are different. + +Now your job is to find the total Hamming distance between all pairs of the +given numbers. + +Example: +Input: 4, 14, 2 + +Output: 6 + +Explanation: In binary representation, the 4 is 0100, 14 is 1110, and 2 is 0010 +(just +showing the four bits relevant in this case). So the answer will be: +HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) += 2 + 2 + 2 = 6. +Note: +Elements of the given array are in the range of 0 to 10^9 +Length of the array will not exceed 10^4. +""" + + +class Solution: + def totalHammingDistance(self, nums): + """ + Brute force, check every combination O(n^2 * b) + + check bit by bit + For each bit, the distance for all is #0 * #1 + O(n * b) + + :type nums: List[int] + :rtype: int + """ + ret = 0 + while any(nums): # any not 0 + z, o = 0, 0 + for i in range(len(nums)): + if nums[i] & 1 == 0: + o += 1 + else: + z += 1 + nums[i] >>= 1 + + ret += z * o + + return ret + + +if __name__ == "__main__": + assert Solution().totalHammingDistance([4, 14, 2]) == 6 From 1cefa9c2aa40ef1927ad6ce8f40129b338dca75c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 27 Jan 2019 12:10:17 -0800 Subject: [PATCH 024/344] 486 --- 486 Predict the Winner.py | 67 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 486 Predict the Winner.py diff --git a/486 Predict the Winner.py b/486 Predict the Winner.py new file mode 100644 index 0000000..1037d93 --- /dev/null +++ b/486 Predict the Winner.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +Given an array of scores that are non-negative integers. Player 1 picks one of +the numbers from either end of the array followed by the player 2 and then +player 1 and so on. Each time a player picks a number, that number will not be +available for the next player. This continues until all the scores have been +chosen. The player with the maximum score wins. + +Given an array of scores, predict whether player 1 is the winner. You can assume +each player plays to maximize his score. + +Example 1: +Input: [1, 5, 2] +Output: False +Explanation: Initially, player 1 can choose between 1 and 2. +If he chooses 2 (or 1), then player 2 can choose from 1 (or 2) and 5. If player +2 chooses 5, then player 1 will be left with 1 (or 2). +So, final score of player 1 is 1 + 2 = 3, and player 2 is 5. +Hence, player 1 will never be the winner and you need to return False. +Example 2: +Input: [1, 5, 233, 7] +Output: True +Explanation: Player 1 first chooses 1. Then player 2 have to choose between 5 +and 7. No matter which number player 2 choose, player 1 can choose 233. +Finally, player 1 has more score (234) than player 2 (12), so you need to return +True representing player1 can win. +Note: +1 <= length of the array <= 20. +Any scores in the given array are non-negative integers and will not exceed +10,000,000. +If the scores of both players are equal, then player 1 is still the winner. +""" +from collections import defaultdict + + +class Solution: + def PredictTheWinner(self, nums): + """ + Let F[i][j] be the max score choose from A[i:j]. The player has two + options + F[i][j] = max( + A[i] + sum(A[i+1:j]) - F[i+1][j], + A[j-1] + sum(A[i:j-1]) - F[i][j-1] + ) + Compare F[0][n] and sum(A) - F[0][n] t0 determine the winner + + :type nums: List[int] + :rtype: bool + """ + l = len(nums) + gross = [0] # sum [0:i] + for e in nums: + gross.append(gross[-1] + e) + + F = defaultdict(lambda: defaultdict(int)) + for i in range(l-1, -1, -1): + for j in range(i+1, l+1): + F[i][j] = max( + gross[j] - gross[i] - F[i+1][j], + gross[j] - gross[i] - F[i][j-1] + ) + return F[0][l] >= (gross[-1] - F[0][l]) + + +if __name__ == "__main__": + assert Solution().PredictTheWinner([1, 5, 2]) == False + assert Solution().PredictTheWinner([1, 5, 233, 7]) == True From 52139ce1aa8038e2a05655ff23c32ce61277a6fa Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 27 Jan 2019 15:46:10 -0800 Subject: [PATCH 025/344] 491 --- 491 Increasing Subsequences.py | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 491 Increasing Subsequences.py diff --git a/491 Increasing Subsequences.py b/491 Increasing Subsequences.py new file mode 100644 index 0000000..e48f3c8 --- /dev/null +++ b/491 Increasing Subsequences.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +""" +Given an integer array, your task is to find all the different possible +increasing subsequences of the given array, and the length of an increasing +subsequence should be at least 2 . + +Example: +Input: [4, 6, 7, 7] +Output: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], +[4,7,7]] +Note: +The length of the given array will not exceed 15. +The range of integer in the given array is [-100,100]. +The given array may contain duplicates, and two equal integers should also be +considered as a special case of increasing sequence. +""" + + +class Solution: + def findSubsequences(self, nums): + """ + 2nd approach + Maintain the current increasing subsequence and iterate them to grow it + Same complexity as the 1st approach since both needs to iterate the + subsequences + + :type nums: List[int] + :rtype: List[List[int]] + """ + subs = set() + for n in nums: + subs |= set([ + sub + (n,) + for sub in subs + if n >= sub[-1] + ]) + subs.add((n,)) + + return [ + list(sub) + for sub in subs + if len(sub) >= 2 + ] + + + def findSubsequences(self, nums): + """ + 1st approach. + F[i] records the increasing subsequence ends at A[i] + F[i] = F[j] + A[i]. + iterating F[j] is exponential + + :type nums: List[int] + :rtype: List[List[int]] + """ + l = len(nums) + F = [ + [(nums[i],)] + for i in range(l) + ] + ret = set() + for i in range(1, l): + for j in range(i): + if nums[i] >= nums[j]: + for t in F[j]: + cur = t + (nums[i],) + ret.add(cur) + F[i].append(cur) + + return list(map(list, ret)) From 3ef91d49752a499434cde09f4f3864da3b054cc3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 27 Jan 2019 16:10:31 -0800 Subject: [PATCH 026/344] 494 --- 494 Target Sum.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 494 Target Sum.py diff --git a/494 Target Sum.py b/494 Target Sum.py new file mode 100644 index 0000000..cb10d80 --- /dev/null +++ b/494 Target Sum.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +""" +You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. +Now you have 2 symbols + and -. For each integer, you should choose one from + +and - as its new symbol. + +Find out how many ways to assign symbols to make sum of integers equal to target +S. + +Example 1: +Input: nums is [1, 1, 1, 1, 1], S is 3. +Output: 5 +Explanation: + +-1+1+1+1+1 = 3 ++1-1+1+1+1 = 3 ++1+1-1+1+1 = 3 ++1+1+1-1+1 = 3 ++1+1+1+1-1 = 3 + +There are 5 ways to assign symbols to make the sum of nums be target 3. +Note: +The length of the given array is positive and will not exceed 20. +The sum of elements in the given array will not exceed 1000. +Your output answer is guaranteed to be fitted in a 32-bit integer. +""" +from collections import defaultdict + + +class Solution: + def findTargetSumWays(self, A, S): + """ + Let F[i][k] be number of ways for A[:i] sum to k + F[i][k] = F[i-1][k-A[i-1]] + F[i-1][k+A[i-1]] + + :type A: List[int] + :type S: int + :rtype: int + """ + if not A: + return + F = defaultdict(lambda: defaultdict(int)) + F[0][0] = 1 + for i in range(len(A)): + for k in F[i].keys(): # F[i] for A[:i] + F[i+1][k-A[i]] += F[i][k] + F[i+1][k+A[i]] += F[i][k] + + return F[len(A)][S] + + +if __name__ == "__main__": + assert Solution().findTargetSumWays([1, 1, 1, 1, 1], 3) == 5 From 145eb8e74fca465badc6b0beb4f9905b1d1cca3d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 27 Jan 2019 17:04:06 -0800 Subject: [PATCH 027/344] 498 --- 498 Diagonal Traverse.py | 95 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 498 Diagonal Traverse.py diff --git a/498 Diagonal Traverse.py b/498 Diagonal Traverse.py new file mode 100644 index 0000000..5378b70 --- /dev/null +++ b/498 Diagonal Traverse.py @@ -0,0 +1,95 @@ +#!/usr/bin/python3 +""" +Given a matrix of M x N elements (M rows, N columns), return all elements of the +matrix in diagonal order as shown in the below image. + +Example: + +Input: +[ + [ 1, 2, 3 ], + [ 4, 5, 6 ], + [ 7, 8, 9 ] +] + +Output: [1,2,4,7,5,3,6,8,9] +""" + + +class Solution: + def findDiagonalOrder(self, matrix): + """ + 2nd approach + diagonal - i + j is constant + let F[i + j] maintain a list of number with subscript (i, j) + + :type matrix: List[List[int]] + :rtype: List[int] + """ + if not matrix: + return [] + + R, C = len(matrix), len(matrix[0]) + F = [[] for _ in range(R+C-1)] + for r in range(R): + for c in range(C): + F[r+c].append(matrix[r][c]) + + ret = [] + for i in range(R+C-1): + if i % 2 == 1: + ret.extend(F[i]) + else: + ret.extend(F[i][::-1]) + + return ret + + def findDiagonalOrder_2(self, matrix): + """ + 1st approach + try 2 * 4 and 4 * 2 and 3 * 3 matrix to find the pattern + + :type matrix: List[List[int]] + :rtype: List[int] + """ + if not matrix: + return [] + i = 0 + j = 0 + inc = True + ret = [] + R, C = len(matrix), len(matrix[0]) + while True: + ret.append(matrix[i][j]) + if i == R - 1 and j == C - 1: + break + if inc: + i -= 1 + j += 1 + if i < 0 or j >= C: + inc = False + if i < 0 and j < C: + i = 0 + else: + i += 2 + j = C - 1 + else: + i += 1 + j -= 1 + if i >= R or j < 0: + inc = True + if j < 0 and i < R: + j = 0 + else: + i = R - 1 + j += 2 + + return ret + + +if __name__ == "__main__": + assert Solution().findDiagonalOrder([ + [ 1, 2, 3 ], + [ 4, 5, 6 ], + [ 7, 8, 9 ] + ]) == [1,2,4,7,5,3,6,8,9] From c10b5bbf0bbc256768ea2478203f19b672bd299c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 27 Jan 2019 17:08:35 -0800 Subject: [PATCH 028/344] 504 --- 504 Base 7.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 504 Base 7.py diff --git a/504 Base 7.py b/504 Base 7.py new file mode 100644 index 0000000..16572b3 --- /dev/null +++ b/504 Base 7.py @@ -0,0 +1,40 @@ +#!/usr/bin/python3 +""" +Given an integer, return its base 7 string representation. + +Example 1: +Input: 100 +Output: "202" +Example 2: +Input: -7 +Output: "-10" + +Note: The input will be in range of [-1e7, 1e7]. +""" + + +class Solution: + def convertToBase7(self, num): + """ + simplfied for negative number + :type num: int + :rtype: str + """ + if num == 0: + return "0" + ret = [] + n = abs(num) + while n: + ret.append(n % 7) + n //= 7 + + ret = "".join(map(str, ret[::-1])) + if num < 0: + ret = "-" + ret + + return ret + + +if __name__ == "__main__": + assert Solution().convertToBase7(100) == "202" + assert Solution().convertToBase7(-7) == "-10" From d3883050f0d0efec8c74a74ba4b9f2e680db17bd Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 27 Jan 2019 18:18:47 -0800 Subject: [PATCH 029/344] 503 --- 503 Next Greater Element II.py | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 503 Next Greater Element II.py diff --git a/503 Next Greater Element II.py b/503 Next Greater Element II.py new file mode 100644 index 0000000..ee175a0 --- /dev/null +++ b/503 Next Greater Element II.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +""" +Given a circular array (the next element of the last element is the first +element of the array), print the Next Greater Number for every element. The Next +Greater Number of a number x is the first greater number to its traversing-order +next in the array, which means you could search circularly to find its next +greater number. If it doesn't exist, output -1 for this number. + +Example 1: +Input: [1,2,1] +Output: [2,-1,2] +Explanation: The first 1's next greater number is 2; +The number 2 can't find next greater number; +The second 1's next greater number needs to search circularly, which is also 2. +Note: The length of given array won't exceed 10000. +""" +from bisect import bisect + + +class Solution: + def nextGreaterElements(self, nums): + """ + scan the nums from right to left, since next largest number, you can + drop certain information about the A[i:]. Use stack to keep a increasing + numbers. if A[i] > any A[i+1: j] but A[i] < A[j], we can safely drop + the numbers A[i+1:j] since they won't be useful. + + :type nums: List[int] + :rtype: List[int] + """ + # initalize the stack + stk = [] + for n in nums[::-1]: + while stk and stk[-1] <= n: + stk.pop() + stk.append(n) + + ret = [] + for n in nums[::-1]: + while stk and stk[-1] <= n: + stk.pop() + ret.append(stk[-1] if stk else -1) + stk.append(n) + + return ret[::-1] + + def nextGreaterElements_error(self, nums): + """ + brute force O(n^2) + + bisect O(n lgn) - error cannot binary search + :type nums: List[int] + :rtype: List[int] + """ + A = nums + nums + print(A) + ret = [] + for e in nums: + t = bisect(A, e) + if t == len(A): + ret.append(-1) + else: + ret.append(A[t]) + + print(ret) + return ret + + +if __name__ == "__main__": + assert Solution().nextGreaterElements([1,2,1]) == [2, -1, 2] From 28aada32e57007adbc49f986076db336d9a7f6a9 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 27 Jan 2019 19:43:02 -0800 Subject: [PATCH 030/344] 501 --- 501 Find Mode in Binary Search Tree.py | 112 +++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 501 Find Mode in Binary Search Tree.py diff --git a/501 Find Mode in Binary Search Tree.py b/501 Find Mode in Binary Search Tree.py new file mode 100644 index 0000000..0498925 --- /dev/null +++ b/501 Find Mode in Binary Search Tree.py @@ -0,0 +1,112 @@ +#!/usr/bin/python3 +""" +Given a binary search tree (BST) with duplicates, find all the mode(s) (the most +frequently occurred element) in the given BST. + +Assume a BST is defined as follows: + +The left subtree of a node contains only nodes with keys less than or equal to +the node's key. +The right subtree of a node contains only nodes with keys greater than or equal +to the node's key. +Both the left and right subtrees must also be binary search trees. + + +For example: +Given BST [1,null,2,2], + + 1 + \ + 2 + / + 2 + + +return [2]. + +Note: If a tree has more than one mode, you can return them in any order. + +Follow up: Could you do that without using any extra space? (Assume that the +implicit stack space incurred due to recursion does not count). +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def findMode(self, root): + """ + In-order traversal + O(1) space thus cannot keep a set of current modes + need two passes - 1st pass to find the mode count, 2nd pass to find all + modes equal to the mode count + + :type root: TreeNode + :rtype: List[int] + """ + ret = [[], 0] # [results, length] + self.find_mode(root, [None, 0], ret, False) + self.find_mode(root, [None, 0], ret, True) + return ret[0] + + def find_mode(self, root, prev, ret, collect): + """ + prev: [previous_value, count]. Need to survice the call stack + """ + if not root: + return + + self.find_mode(root.left, prev, ret, collect) + + if prev[0] == root.val: + prev[1] += 1 + else: + prev[1] = 1 + prev[0] = root.val + + if not collect: + ret[1] = max(ret[1], prev[1]) + else: + if prev[1] == ret[1]: + ret[0].append(root.val) + + self.find_mode(root.right, prev, ret, collect) + + def findMode_error(self, root): + """ + counter (extra space) for any tree + use recursion + + :type root: TreeNode + :rtype: List[int] + """ + if not root: + return [] + + ret = [0, []] + self.find_mode_error(root, root.val, ret) + return ret[1] + + def find_mode_error(self, root, target, ret): + cur = 0 + if not root: + return cur + + if root.val == target: + cur += 1 + cur += self.find_mode_error(root.left, root.val, ret) + cur += self.find_mode_error(root.right, root.val, ret) + if cur > ret[0]: + ret[0], ret[1] = cur, [target] + elif cur == ret[0]: + ret[1].append(target) + else: + self.find_mode_error(root, root.val, ret) + + return cur From 026aa4663e81d6198fd504ec09caa4ae70416e0f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 29 Jan 2019 00:24:00 -0800 Subject: [PATCH 031/344] 513 --- 513 Find Bottom Left Tree Value.py | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 513 Find Bottom Left Tree Value.py diff --git a/513 Find Bottom Left Tree Value.py b/513 Find Bottom Left Tree Value.py new file mode 100644 index 0000000..1e48ee5 --- /dev/null +++ b/513 Find Bottom Left Tree Value.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +""" +Given a binary tree, find the leftmost value in the last row of the tree. + +Example 1: +Input: + + 2 + / \ + 1 3 + +Output: +1 +Example 2: +Input: + + 1 + / \ + 2 3 + / / \ + 4 5 6 + / + 7 + +Output: +7 +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def findBottomLeftValue(self, root): + """ + BFS + + :type root: TreeNode + :rtype: int + """ + q = [root] + while q: + ret = q[0].val + cur_q = [] + for e in q: + if e.left: + cur_q.append(e.left) + if e.right: + cur_q.append(e.right) + q = cur_q + + return ret From 09245ae5407cce360f5533c61a9214e45c2960a5 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 29 Jan 2019 00:31:14 -0800 Subject: [PATCH 032/344] 508 --- 508 Most Frequent Subtree Sum.py | 67 ++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 508 Most Frequent Subtree Sum.py diff --git a/508 Most Frequent Subtree Sum.py b/508 Most Frequent Subtree Sum.py new file mode 100644 index 0000000..910fd94 --- /dev/null +++ b/508 Most Frequent Subtree Sum.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +Given the root of a tree, you are asked to find the most frequent subtree sum. +The subtree sum of a node is defined as the sum of all the node values formed by +the subtree rooted at that node (including the node itself). So what is the most +frequent subtree sum value? If there is a tie, return all the values with the +highest frequency in any order. + +Examples 1 +Input: + + 5 + / \ +2 -3 +return [2, -3, 4], since all the values happen only once, return all of them in +any order. +Examples 2 +Input: + + 5 + / \ +2 -5 +return [2], since 2 happens twice, however -5 only occur once. +Note: You may assume the sum of values in any subtree is in the range of 32-bit +signed integer. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +from collections import defaultdict + + +class Solution: + def findFrequentTreeSum(self, root): + """ + traverse with counter + :type root: TreeNode + :rtype: List[int] + """ + counter = defaultdict(int) + self.traverse(root, counter) + ret = [[], 0] + for k, v in counter.items(): + if v > ret[1]: + ret[0] = [k] + ret[1] = v + elif v == ret[1]: + ret[0].append(k) + + return ret[0] + + def traverse(self, root, counter): + if not root: + return 0 + + cur = root.val + cur += self.traverse(root.left, counter) + cur += self.traverse(root.right, counter) + counter[cur] += 1 + return cur From cca78e8e421d36e1f0c398f44ab29b849b22ddbb Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 30 Jan 2019 22:12:10 -0800 Subject: [PATCH 033/344] 515 --- 515 Find Largest Value in Each Tree Row.py | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 515 Find Largest Value in Each Tree Row.py diff --git a/515 Find Largest Value in Each Tree Row.py b/515 Find Largest Value in Each Tree Row.py new file mode 100644 index 0000000..e2bd561 --- /dev/null +++ b/515 Find Largest Value in Each Tree Row.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" +You need to find the largest value in each row of a binary tree. + +Example: +Input: + + 1 + / \ + 3 2 + / \ \ + 5 3 9 + +Output: [1, 3, 9] +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def largestValues(self, root): + """ + BFS + :type root: TreeNode + :rtype: List[int] + """ + ret = [] + if not root: + return ret + + q = [root] + while q: + ret.append(max(map(lambda e: e.val, q))) + cur_q = [] + for e in q: + if e.left: + cur_q.append(e.left) + if e.right: + cur_q.append(e.right) + + q = cur_q + + return ret From c3ef3640e41a4acc284eec24ffc8b4017191febf Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 30 Jan 2019 22:32:00 -0800 Subject: [PATCH 034/344] 523 --- 523. Continuous Subarray Sum.py | 50 +++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 523. Continuous Subarray Sum.py diff --git a/523. Continuous Subarray Sum.py b/523. Continuous Subarray Sum.py new file mode 100644 index 0000000..65b61b4 --- /dev/null +++ b/523. Continuous Subarray Sum.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +""" +Given a list of non-negative numbers and a target integer k, write a function to +check if the array has a continuous subarray of size at least 2 that sums up to +the multiple of k, that is, sums up to n*k where n is also an integer. + +Example 1: +Input: [23, 2, 4, 6, 7], k=6 +Output: True +Explanation: Because [2, 4] is a continuous subarray of size 2 and sums up to 6. +Example 2: +Input: [23, 2, 6, 4, 7], k=6 +Output: True +Explanation: Because [23, 2, 6, 4, 7] is an continuous subarray of size 5 and + sums up to 42. +Note: +The length of the array won't exceed 10,000. +You may assume the sum of all the numbers is in the range of a signed 32-bit +integer. +""" + + +class Solution: + def checkSubarraySum(self, nums, k): + """ + Two pointers algorithm won't work since it is multiple of k + + prefix sum + hashmap (look back) + mod k (Math) + :type nums: List[int] + :type k: int + :rtype: bool + """ + h = {0: 0} # [:l], half open, factor in trival case + s = 0 + for l in range(1, len(nums) + 1): + s += nums[l-1] + if k != 0: # edge case + s %= k + if s in h: + if l - h[s] >= 2: + return True + else: + # only keep the lowest + h[s] = l + + return False + + +if __name__ == "__main__": + assert Solution().checkSubarraySum([23,2,4,6,7], 6) == True From cbbd4a67ab342ada2421e13f82d660b1d47d4d20 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 31 Jan 2019 00:13:58 -0800 Subject: [PATCH 035/344] 516 518 --- 516 Longest Palindromic Subsequence.py | 53 ++++++++++++ 518 Coin Change 2.py | 108 +++++++++++++++++++++++++ 523. Continuous Subarray Sum.py | 2 +- 3 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 516 Longest Palindromic Subsequence.py create mode 100644 518 Coin Change 2.py diff --git a/516 Longest Palindromic Subsequence.py b/516 Longest Palindromic Subsequence.py new file mode 100644 index 0000000..0964b59 --- /dev/null +++ b/516 Longest Palindromic Subsequence.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +""" +Given a string s, find the longest palindromic subsequence's length in s. You +may assume that the maximum length of s is 1000. + +Example 1: +Input: + +"bbbab" +Output: +4 +One possible longest palindromic subsequence is "bbbb". +Example 2: +Input: + +"cbbd" +Output: +2 +One possible longest palindromic subsequence is "bb". +""" +from collections import defaultdict + + +class Solution: + def longestPalindromeSubseq(self, s): + """ + Brute force 0-1, exponential + + F[i][j] be the longest palindrom subseuence (not necessarily ending at + A[i] A[j]) + + F[i-1]F[j+1] = F[i][j] + 2 or F[i][j] # error + The transition function shoud be + F[i][j] = F[i+1][j-1] + 2 or F[i+1][j] or F[i][j-1] + :type s: str + :rtype: int + """ + n = len(s) + F = defaultdict(lambda: defaultdict(int)) + for i in range(n): + F[i][i] = 1 + + for i in range(n-1, -1, -1): + for j in range(i+1, n): + F[i][j] = max(F[i+1][j], F[i][j-1]) + if s[i] == s[j]: + F[i][j] = max(F[i][j], F[i+1][j-1] + 2) + + return F[0][n-1] + + +if __name__ == "__main__": + assert Solution().longestPalindromeSubseq("bbbab") == 4 diff --git a/518 Coin Change 2.py b/518 Coin Change 2.py new file mode 100644 index 0000000..8928912 --- /dev/null +++ b/518 Coin Change 2.py @@ -0,0 +1,108 @@ +#!/usr/bin/python3 +""" +You are given coins of different denominations and a total amount of money. +Write a function to compute the number of combinations that make up that amount. +You may assume that you have infinite number of each kind of coin. + + + +Example 1: + +Input: amount = 5, coins = [1, 2, 5] +Output: 4 +Explanation: there are four ways to make up the amount: +5=5 +5=2+2+1 +5=2+1+1+1 +5=1+1+1+1+1 +Example 2: + +Input: amount = 3, coins = [2] +Output: 0 +Explanation: the amount of 3 cannot be made up just with coins of 2. +Example 3: + +Input: amount = 10, coins = [10] +Output: 1 +""" +from collections import defaultdict + + +class Solution: + def change(self, amount, coins): + """ + let F[amount][l] = # ways ending (but not necesserily) using coins[l-1] + (i.e. coins[:l]) + Two options: use coin[i] or not + F[amount][l] = F[amount][l-1] + F[amount - coin[l-1]][l] + + Similar to 0-1 knapsack + + :type amount: int + :type coins: List[int] + :rtype: int + """ + F = defaultdict(lambda: defaultdict(int)) + n = len(coins) + for l in range(n + 1): + F[0][l] = 1 # trival case + + for a in range(1, amount + 1): + for l in range(1, n + 1): + F[a][l] = F[a][l-1] + F[a - coins[l-1]][l] + + return F[amount][n] + + + def change_TLE(self, amount, coins): + """ + Like the take the step for the stairs dp + + let F[amount][i] = # ways ending using coin[i] + F[amount][i] += F[amount - coin[i]][j] for j in range(i) + + O(n^3) + :type amount: int + :type coins: List[int] + :rtype: int + """ + if amount == 0: + return 1 + + coins.sort() + n = len(coins) + F = defaultdict(lambda: defaultdict(int)) + for i in range(n): + F[coins[i]][i] = 1 + + for a in range(1, amount + 1): + for i in range(n): + for j in range(i + 1): + F[a][i] += F[a - coins[i]][j] + + return sum(F[amount].values()) + + def change_error(self, amount, coins): + """ + Like the take the step for the stairs dp + + let F[amount] = # ways + F[amount] += F[amount - v] for v in coins + error: count repeated: 1 + 2, 2 + 1 + + :type amount: int + :type coins: List[int] + :rtype: int + """ + F = {0: 1} + for a in range(1, amount + 1): + F[a] = 0 + for c in coins: + if a - c in F: + F[a] += F[a - c] + + return F[amount] + + +if __name__ == "__main__": + assert Solution().change(5, [1, 2, 5]) == 4 diff --git a/523. Continuous Subarray Sum.py b/523. Continuous Subarray Sum.py index 65b61b4..346c7c3 100644 --- a/523. Continuous Subarray Sum.py +++ b/523. Continuous Subarray Sum.py @@ -37,7 +37,7 @@ def checkSubarraySum(self, nums, k): if k != 0: # edge case s %= k if s in h: - if l - h[s] >= 2: + if l - h[s] >= 2: # size at least 2 return True else: # only keep the lowest From 83aa98f73b92ea021fc4f220686762ae79f2ce82 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 1 Feb 2019 00:44:16 -0800 Subject: [PATCH 036/344] 647 --- 518 Coin Change 2.py | 4 +-- 647 Palindromic Substrings.py | 59 +++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 647 Palindromic Substrings.py diff --git a/518 Coin Change 2.py b/518 Coin Change 2.py index 8928912..9051483 100644 --- a/518 Coin Change 2.py +++ b/518 Coin Change 2.py @@ -33,7 +33,7 @@ def change(self, amount, coins): """ let F[amount][l] = # ways ending (but not necesserily) using coins[l-1] (i.e. coins[:l]) - Two options: use coin[i] or not + Two options: use coin[l-1] or not F[amount][l] = F[amount][l-1] + F[amount - coin[l-1]][l] Similar to 0-1 knapsack @@ -45,7 +45,7 @@ def change(self, amount, coins): F = defaultdict(lambda: defaultdict(int)) n = len(coins) for l in range(n + 1): - F[0][l] = 1 # trival case + F[0][l] = 1 # trivial case for a in range(1, amount + 1): for l in range(1, n + 1): diff --git a/647 Palindromic Substrings.py b/647 Palindromic Substrings.py new file mode 100644 index 0000000..582b656 --- /dev/null +++ b/647 Palindromic Substrings.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 +""" +Given a string, your task is to count how many palindromic substrings in this +string. + +The substrings with different start indexes or end indexes are counted as +different substrings even they consist of same characters. + +Example 1: +Input: "abc" +Output: 3 +Explanation: Three palindromic strings: "a", "b", "c". +Example 2: +Input: "aaa" +Output: 6 +Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa". +Note: +The input string length won't exceed 1000. +""" +from collections import defaultdict + + +class Solution: + def countSubstrings(self, s): + """ + for every s[i:j], check whether it is a palindrome + O(n^2 * n) + + DP + Let F[i][j] be whether s[i:j] is a palindrome + F[i][j] = F[i+1][j-1] if s[i] == s[j-1] + else False + + :type s: str + :rtype: int + """ + F = defaultdict(lambda: defaultdict(bool)) + n = len(s) + for i in range(n): + F[i][i] = True + F[i][i+1] = True + + for i in range(n-1, -1, -1): + for j in range(i+2, n+1): + if s[i] == s[j-1]: + F[i][j] = F[i+1][j-1] + else: + F[i][j] = False + + return sum( + 1 + for i in range(n) + for j in range(i+1, n+1) + if F[i][j] + ) + + +if __name__ == "__main__": + assert Solution().countSubstrings("aaa") == 6 From 36c40f7a69181460993f1ca6a44e6255979eecd3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Feb 2019 18:00:54 -0800 Subject: [PATCH 037/344] 525 --- 525 Contiguous Array.py | 109 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 525 Contiguous Array.py diff --git a/525 Contiguous Array.py b/525 Contiguous Array.py new file mode 100644 index 0000000..7ca75e0 --- /dev/null +++ b/525 Contiguous Array.py @@ -0,0 +1,109 @@ +#!/usr/bin/python3 +""" +Given a binary array, find the maximum length of a contiguous subarray with +equal number of 0 and 1. + +Example 1: +Input: [0,1] +Output: 2 +Explanation: [0, 1] is the longest contiguous subarray with equal number of 0 +and 1. +Example 2: +Input: [0,1,0] +Output: 2 +Explanation: [0, 1] (or [1, 0]) is a longest contiguous subarray with equal +number of 0 and 1. +Note: The length of the given binary array will not exceed 50,000. +""" + + +class Solution: + def findMaxLength(self, nums): + """ + Look back with map + + key: map stores the difference of the 0, 1 count + Similar to contiguous sum to target + :type nums: List[int] + :rtype: int + """ + o = 0 + z = 0 + d = {0: 0} # diff for nums[:l] + ret = 0 + for i, e in enumerate(nums): + if e == 1: + o += 1 + else: + z += 1 + diff = o - z + if diff in d: + ret = max(ret, i + 1 - d[diff]) + else: + d[diff] = i + 1 + + return ret + + def findMaxLength_error(self, nums): + """ + starting from both sides, shrinking until equal + + :type nums: List[int] + :rtype: int + """ + n = len(nums) + F = [0 for _ in range(n+1)] + for i in range(n): + F[i+1] = F[i] + if nums[i] == 0: + F[i+1] += 1 + + i = 0 + j = n + while i < j: + count = F[j] - F[i] + l = j - i + if count * 2 == l: + print(l) + return l + elif count * 2 < l: + if nums[i] == 1: + i += 1 + else: + j -= 1 + else: + if nums[i] == 0: + i += 1 + else: + j -= 1 + return 0 + + + def findMaxLength_TLE(self, nums): + """ + scan nums[i:j], check number of 0 (pre-calculated) + O(n^2) + + :type nums: List[int] + :rtype: int + """ + F = [0] + n = len(nums) + for e in nums: + if e == 0: + F.append(F[-1] + 1) + else: + F.append(F[-1]) + + ret = 0 + for i in range(n): + for j in range(i+1, n+1): + if (F[j] - F[i]) * 2 == j - i: + ret = max(ret, j - i) + + return ret + + +if __name__ == "__main__": + assert Solution().findMaxLength([0, 1, 0]) == 2 + assert Solution().findMaxLength([0,1,0,1,1,1,0,0,1,1,0,1,1,1,1,1,1,0,1,1,0,1,1,0,0,0,1,0,1,0,0,1,0,1,1,1,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,1,0,0,1,1,1,1,1,0,0,1,1,1,1,0,0,1,0,1,1,0,0,0,0,0,0,1,0,1,0,1,1,0,0,1,1,0,1,1,1,1,0,1,1,0,0,0,1,1]) == 68 From bbeef9325d0fd8f3ac2d3bc718c8dd68d4613c1b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Feb 2019 18:14:29 -0800 Subject: [PATCH 038/344] 526 --- 526 Beautiful Arrangement.py | 61 ++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 526 Beautiful Arrangement.py diff --git a/526 Beautiful Arrangement.py b/526 Beautiful Arrangement.py new file mode 100644 index 0000000..ef41c96 --- /dev/null +++ b/526 Beautiful Arrangement.py @@ -0,0 +1,61 @@ +#!/usr/bin/python3 +""" +Suppose you have N integers from 1 to N. We define a beautiful arrangement as an +array that is constructed by these N numbers successfully if one of the +following is true for the ith position (1 <= i <= N) in this array: + +The number at the ith position is divisible by i. +i is divisible by the number at the ith position. + + +Now given N, how many beautiful arrangements can you construct? + +Example 1: + +Input: 2 +Output: 2 +Explanation: + +The first beautiful arrangement is [1, 2]: + +Number at the 1st position (i=1) is 1, and 1 is divisible by i (i=1). + +Number at the 2nd position (i=2) is 2, and 2 is divisible by i (i=2). + +The second beautiful arrangement is [2, 1]: + +Number at the 1st position (i=1) is 2, and 2 is divisible by i (i=1). + +Number at the 2nd position (i=2) is 1, and i (i=2) is divisible by 1. + + +Note: + +N is a positive integer and will not exceed 15. +""" + + +class Solution: + def countArrangement(self, N: int) -> int: + """ + dfs + """ + candidates = set(range(1, N+1)) + ret = self.dfs(candidates, 1, N) + return ret + + def dfs(self, candidates, i, N): + if i > N: + return 1 + + ret = 0 + for c in candidates: + if c % i == 0 or i % c == 0: + candidates.remove(c) + ret += self.dfs(candidates, i+1, N) + candidates.add(c) + return ret + + +if __name__ == "__main__": + assert Solution().countArrangement(2) == 2 From fd28467a9adbf11d4bea64e304bcd25bc4a81fdd Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Feb 2019 21:17:40 -0800 Subject: [PATCH 039/344] 524 --- ...est Word in Dictionary through Deleting.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 524 Longest Word in Dictionary through Deleting.py diff --git a/524 Longest Word in Dictionary through Deleting.py b/524 Longest Word in Dictionary through Deleting.py new file mode 100644 index 0000000..1e8b454 --- /dev/null +++ b/524 Longest Word in Dictionary through Deleting.py @@ -0,0 +1,64 @@ +#!/usr/bin/python3 +""" +Given a string and a string dictionary, find the longest string in the +dictionary that can be formed by deleting some characters of the given string. +If there are more than one possible results, return the longest word with the +smallest lexicographical order. If there is no possible result, return the empty +string. + +Example 1: +Input: +s = "abpcplea", d = ["ale","apple","monkey","plea"] + +Output: +"apple" +Example 2: +Input: +s = "abpcplea", d = ["a","b","c"] + +Output: +"a" +Note: +All the strings in the input will only contain lower-case letters. +The size of the dictionary won't exceed 1,000. +The length of all the strings in the input won't exceed 1,000. +""" +from collections import defaultdict + + +class Solution: + def findLongestWord(self, s, d): + """ + Compare subsequence: O(|S|) (two pointers) + Then iterate d, check subsequence: O(|S||d|) + + Generalize two pointers to n pointers + O(|S||d|) + + + :type s: str + :type d: List[str] + :rtype: str + """ + h = defaultdict(list) + for d_idx, w in enumerate(d): + w_idx = 0 + h[w[w_idx]].append((d_idx, w_idx)) + + ret = "" + for e in s: + lst = h.pop(e, []) + for d_idx, w_idx in lst: + w = d[d_idx] + w_idx += 1 + if w_idx >= len(w): + # if len(w) >= len(ret) and w < ret: # error + ret = min(ret, w, key=lambda x: (-len(x), x)) # compare with primary and secondary key + else: + h[w[w_idx]].append((d_idx, w_idx)) + + return ret + + +if __name__ == "__main__": + assert Solution().findLongestWord("abpcplea", ["ale","apple","monkey","plea"]) == "apple" From 06a440005ce6615bfae7579b7daae115b13304d8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Feb 2019 21:49:49 -0800 Subject: [PATCH 040/344] 530 --- 530 Minimum Absolute Difference in BST.py | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 530 Minimum Absolute Difference in BST.py diff --git a/530 Minimum Absolute Difference in BST.py b/530 Minimum Absolute Difference in BST.py new file mode 100644 index 0000000..12a088e --- /dev/null +++ b/530 Minimum Absolute Difference in BST.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 +""" +Given a binary search tree with non-negative values, find the minimum absolute +difference between values of any two nodes. + +Example: + +Input: + + 1 + \ + 3 + / + 2 + +Output: +1 + +Explanation: +The minimum absolute difference is 1, which is the difference between 2 and 1 (or between 2 and 3). + + +Note: There are at least two nodes in this BST. +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def getMinimumDifference(self, root: 'TreeNode') -> int: + """ + For every node, find min and max in left or right substree. + O(n lgn) + + To optimize: + recursively pass the min and max, O(n) + """ + ret = [float('inf')] # keep reference + self.dfs(root, ret) + return ret[0] + + def dfs(self, node, ret): + if not node: + return None, None + left_min, left_max = self.dfs(node.left, ret) + right_min, right_max = self.dfs(node.right, ret) + if left_max: + ret[0] = min(ret[0], abs(node.val - left_max)) + if right_min: + ret[0] = min(ret[0], abs(node.val - right_min)) + left_min = left_min or node.val + right_max = right_max or node.val + return left_min, right_max From 14db9f8be893146412aba5f3bbb9b1c3055b1daa Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Feb 2019 23:14:08 -0800 Subject: [PATCH 041/344] 538 540 --- 538 Convert BST to Greater Tree.py | 42 ++++++++++++++ 540 Single Element in a Sorted Array.py | 73 +++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 538 Convert BST to Greater Tree.py create mode 100644 540 Single Element in a Sorted Array.py diff --git a/538 Convert BST to Greater Tree.py b/538 Convert BST to Greater Tree.py new file mode 100644 index 0000000..8b4760f --- /dev/null +++ b/538 Convert BST to Greater Tree.py @@ -0,0 +1,42 @@ +#!/usr/bin/python3 +""" +Given a Binary Search Tree (BST), convert it to a Greater Tree such that every +key of the original BST is changed to the original key plus sum of all keys +greater than the original key in BST. + +Example: + +Input: The root of a Binary Search Tree like this: + 5 + / \ + 2 13 + +Output: The root of a Greater Tree like this: + 18 + / \ + 20 13 +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def convertBST(self, root: 'TreeNode') -> 'TreeNode': + """ + in-order traversal, right first + """ + self.walk(root, 0) + return root + + def walk(self, node, cur_sum): + """stateless walk""" + if not node: + return cur_sum + s = self.walk(node.right, cur_sum) + node.val += s + return self.walk(node.left, node.val) diff --git a/540 Single Element in a Sorted Array.py b/540 Single Element in a Sorted Array.py new file mode 100644 index 0000000..c771512 --- /dev/null +++ b/540 Single Element in a Sorted Array.py @@ -0,0 +1,73 @@ +#!/usr/bin/python3 +""" +Given a sorted array consisting of only integers where every element appears +twice except for one element which appears once. Find this single element that +appears only once. + +Example 1: +Input: [1,1,2,3,3,4,4,8,8] +Output: 2 +Example 2: +Input: [3,3,7,7,10,11,11] +Output: 10 +Note: Your solution should run in O(log n) time and O(1) space. +""" +from typing import List +from bisect import bisect_right + + +class Solution: + def singleNonDuplicate(self, nums: List[int]) -> int: + """ + sorted array + + binary search with checking mid odd/even + """ + n = len(nums) + lo, hi = 0, n + while lo < hi: + mid = (lo + hi) // 2 + if ( + mid % 2 == 0 and mid + 1 < hi and nums[mid] == nums[mid + 1] + ) or ( + mid % 2 == 1 and mid - 1 >= lo and nums[mid] == nums[mid - 1] + ): + # to make the target is on the right + # when mid even, mid and mid + 1 form a pair; there are odd number of elements on the right + # when mid odd, mid and mid - 1 form a pair; there are odd number of elements on the right + lo = mid + 1 + else: + hi = mid + + return nums[lo] + + + def singleNonDuplicate_error(self, nums: List[int]) -> int: + """ + sorted array + + consider the expected arry with no exception. The index of each element + should be in the expected position + binary search, compare the searched index and expected index + """ + n = len(nums) + lo, hi = 0, n + while lo < hi: + mid = (lo + hi) // 2 + idx = bisect_right(nums, nums[mid], lo, hi) + if idx <= mid: + hi = mid - 1 + else: + lo = mid + + return nums[hi - 1] + + + def singleNonDuplicate_xor(self, nums: List[int]) -> int: + """ + XOR O(n) + """ + ret = nums[0] + for e in nums[1:]: + ret ^= e + return ret From 57cc83715696db06a085a59c7f4b29adb382de6e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Feb 2019 23:27:10 -0800 Subject: [PATCH 042/344] 669 --- 669 Trim a Binary Search Tree.py | 69 ++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 669 Trim a Binary Search Tree.py diff --git a/669 Trim a Binary Search Tree.py b/669 Trim a Binary Search Tree.py new file mode 100644 index 0000000..5537e25 --- /dev/null +++ b/669 Trim a Binary Search Tree.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +""" +Given a binary search tree and the lowest and highest boundaries as L and R, +trim the tree so that all its elements lies in [L, R] (R >= L). You might need +to change the root of the tree, so the result should return the new root of the +trimmed binary search tree. + +Example 1: +Input: + 1 + / \ + 0 2 + + L = 1 + R = 2 + +Output: + 1 + \ + 2 +Example 2: +Input: + 3 + / \ + 0 4 + \ + 2 + / + 1 + + L = 1 + R = 3 + +Output: + 3 + / + 2 + / + 1 +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def trimBST(self, root: TreeNode, L: int, R: int) -> TreeNode: + """ + post-order traverse + """ + return self.walk(root, L, R) + + def walk(self, node, L, R): + if not node: + return None + + node.left = self.walk(node.left, L, R) + node.right = self.walk(node.right, L, R) + if node.val < L: + return node.right + elif node.val > R: + return node.left + else: + return node From 91b2ff72f5a1e20a8105af81d21c210d1b592d8e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Feb 2019 23:47:34 -0800 Subject: [PATCH 043/344] 539 --- 539 Minimum Time Difference.py | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 539 Minimum Time Difference.py diff --git a/539 Minimum Time Difference.py b/539 Minimum Time Difference.py new file mode 100644 index 0000000..bbacc74 --- /dev/null +++ b/539 Minimum Time Difference.py @@ -0,0 +1,43 @@ +#!/usr/bin/python3 +""" +Given a list of 24-hour clock time points in "Hour:Minutes" format, find the +minimum minutes difference between any two time points in the list. +Example 1: +Input: ["23:59","00:00"] +Output: 1 +Note: +The number of time points in the given list is at least 2 and won't exceed 20000. +The input time is legal and ranges from 00:00 to 23:59. +""" +from typing import List + + +class Solution: + def findMinDifference(self, timePoints: List[str]) -> int: + """ + sort and minus + """ + ret = float("inf") + A = list(sorted(map(self.minutes, timePoints))) + n = len(A) + for i in range(n - 1): + ret = min(ret, self.diff(A[i+1], A[i])) + + ret = min(ret, self.diff(A[n-1], A[0])) + return ret + + def diff(self, b, a): + ret = b - a + if ret > 12 * 60: + ret = 24 * 60 - ret + + return ret + + def minutes(self, a): + h, m = a.split(":") + minutes = 60 * int(h) + int(m) + return minutes + + +if __name__ == "__main__": + assert Solution().findMinDifference(["23:59","00:00"]) == 1 From 97f536342f537256b0b17c2a220c50850993a0c9 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 7 Feb 2019 23:31:27 -0800 Subject: [PATCH 044/344] 496 --- 496 Next Greater Element I.py | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 496 Next Greater Element I.py diff --git a/496 Next Greater Element I.py b/496 Next Greater Element I.py new file mode 100644 index 0000000..36760da --- /dev/null +++ b/496 Next Greater Element I.py @@ -0,0 +1,61 @@ +#!/usr/bin/python3 +""" +You are given two arrays (without duplicates) nums1 and nums2 where nums1’s +enclements are subset of nums2. Find all the next greater numbers for nums1's +elements in the corresponding places of nums2. + +The Next Greater Number of a number x in nums1 is the first greater number to +its right in nums2. If it does not exist, output -1 for this number. + +Example 1: +Input: nums1 = [4,1,2], nums2 = [1,3,4,2]. +Output: [-1,3,-1] +Explanation: + For number 4 in the first array, you cannot find the next greater number for it in the second array, so output -1. + For number 1 in the first array, the next greater number for it in the second array is 3. + For number 2 in the first array, there is no next greater number for it in the second array, so output -1. +Example 2: +Input: nums1 = [2,4], nums2 = [1,2,3,4]. +Output: [3,-1] +Explanation: + For number 2 in the first array, the next greater number for it in the second array is 3. + For number 4 in the first array, there is no next greater number for it in the second array, so output -1. +Note: +All elements in nums1 and nums2 are unique. +The length of both nums1 and nums2 would not exceed 1000. +""" + + +class Solution: + def nextGreaterElement(self, nums1, nums2): + """ + Brute force O(mn) + + Need to understand the problem first. + nums1 is just query + nums2 is the target look up + + When look at A[i], need the information of increasing subsequence + from A[i+1:] + + Use a stack to maintain the increasing subsequence with top with min, + bottom max + + :type nums1: List[int] + :type nums2: List[int] + :rtype: List[int] + """ + h = {} # num -> next greater element + stk = [] + for e in nums2[::-1]: + while stk and stk[-1] <= e: + # until stk[-1] > e + stk.pop() + + h[e] = stk[-1] if stk else -1 + stk.append(e) + + return [ + h[q] + for q in nums1 + ] From 2199e1221b1982707edb693bdd8a622b93976975 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 8 Feb 2019 00:24:26 -0800 Subject: [PATCH 045/344] 556 --- 030 Next Permutation.py | 3 ++ 556 Next Greater Element III.py | 90 +++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 556 Next Greater Element III.py diff --git a/030 Next Permutation.py b/030 Next Permutation.py index c6a3202..5e0095e 100644 --- a/030 Next Permutation.py +++ b/030 Next Permutation.py @@ -11,6 +11,8 @@ 1,1,5 -> 1,5,1 """ __author__ = 'Danyang' + + class Solution: def nextPermutation(self, num): """ @@ -45,5 +47,6 @@ def nextPermutation(self, num): num[partition_num_index+1:] = reversed(num[partition_num_index+1:]) return num + if __name__=="__main__": print Solution().nextPermutation([3, 2, 1]) diff --git a/556 Next Greater Element III.py b/556 Next Greater Element III.py new file mode 100644 index 0000000..df77185 --- /dev/null +++ b/556 Next Greater Element III.py @@ -0,0 +1,90 @@ +#!/usr/bin/python3 +""" +Given a positive 32-bit integer n, you need to find the smallest 32-bit integer +which has exactly the same digits existing in the integer n and is greater in +value than n. If no such positive 32-bit integer exists, you need to return -1. + +Example 1: + +Input: 12 +Output: 21 + + +Example 2: + +Input: 21 +Output: -1 +""" + + +class Solution: + def nextGreaterElement(self, n: int) -> int: + """ + next permutation + + http://fisherlei.blogspot.com/2012/12/leetcode-next-permutation.html + + why reverse? reverse the increasing from right to left to decreasing + from right to left (i.e. sorted) + """ + seq = list(str(n)) + N = len(seq) + if N < 2: + return -1 + + # from right to left + i = N - 2 + while seq[i] >= seq[i+1]: + i -= 1 + if i < 0: + return -1 + + j = N - 1 + while seq[i] >= seq[j]: + j -= 1 + + seq[i], seq[j] = seq[j], seq[i] + seq[i+1:] = reversed(seq[i+1:]) + ret = int("".join(seq)) + if ret <= 1 << 31 - 1: + return ret + else: + return -1 + + def nextGreaterElement_sort(self, n: int) -> int: + """ + Looking at the decimal digits rather than binary digits + + 2 8 4 1 + 4 1 2 8 + + 2 3 4 1 + 2 4 1 3 + + from right to left + find the first digit that has min larger, then sort the rest + """ + seq = [int(e) for e in str(n)] + stk = [] # record index + for i in range(len(seq) - 1, -1 , -1): + e = seq[i] + popped = None + while stk and seq[stk[-1]] > e: + popped = stk.pop() + + if popped: + seq[i], seq[popped] = seq[popped], seq[i] + seq[i+1:] = sorted(seq[i+1:]) # reversed also good + ret = int("".join(map(str, seq))) + if ret <= 1 << 31 - 1: + return ret + else: + return -1 + + stk.append(i) + + return -1 + + +if __name__ == "__main__": + assert Solution().nextGreaterElement(12) == 21 From 5582d8328f696095095d78b9cba2cbcf92a88a80 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 9 Feb 2019 16:05:20 -0800 Subject: [PATCH 046/344] 557 --- 557 Reverse Words in a String III.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 557 Reverse Words in a String III.py diff --git a/557 Reverse Words in a String III.py b/557 Reverse Words in a String III.py new file mode 100644 index 0000000..e69de29 From 2cabebf38cb941c11996d5fc7e9170bed326a21e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 9 Feb 2019 16:05:54 -0800 Subject: [PATCH 047/344] 557 --- 557 Reverse Words in a String III.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/557 Reverse Words in a String III.py b/557 Reverse Words in a String III.py index e69de29..4e80186 100644 --- a/557 Reverse Words in a String III.py +++ b/557 Reverse Words in a String III.py @@ -0,0 +1,16 @@ +#!/usr/bin/python3 +""" +Given a string, you need to reverse the order of characters in each word within +a sentence while still preserving whitespace and initial word order. + +Example 1: +Input: "Let's take LeetCode contest" +Output: "s'teL ekat edoCteeL tsetnoc" +Note: In the string, each word is separated by single space and there will not +be any extra space in the string. +""" + + +class Solution: + def reverseWords(self, s: str) -> str: + return " ".join(map(lambda x: x[::-1], s.split(" "))) From a2b0c9bd2c3602b9415f1a51cf31ee815f94444b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 9 Feb 2019 16:14:07 -0800 Subject: [PATCH 048/344] 559 --- 559 Maximum Depth of N-ary Tree.py | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 559 Maximum Depth of N-ary Tree.py diff --git a/559 Maximum Depth of N-ary Tree.py b/559 Maximum Depth of N-ary Tree.py new file mode 100644 index 0000000..302f403 --- /dev/null +++ b/559 Maximum Depth of N-ary Tree.py @@ -0,0 +1,36 @@ +#!/usr/bin/python3 +""" +Given a n-ary tree, find its maximum depth. + +The maximum depth is the number of nodes along the longest path from the root +node down to the farthest leaf node. + +For example, given a 3-ary tree: + +We should return its max depth, which is 3. + +Note: + +The depth of the tree is at most 1000. +The total number of nodes is at most 5000. +""" + + +# Definition for a Node. +class Node: + def __init__(self, val, children): + self.val = val + self.children = children + + +class Solution: + def maxDepth(self, root: "Node") -> int: + if not root: + return 0 + + max_child_depth = max([ + self.maxDepth(child) + for child in root.children + ] or [0]) + + return max_child_depth + 1 From dd6f185bb87417f7a75196705a3aff0b6fc0925c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 9 Feb 2019 16:32:17 -0800 Subject: [PATCH 049/344] 561 --- 561 Array Partition I.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 561 Array Partition I.py diff --git a/561 Array Partition I.py b/561 Array Partition I.py new file mode 100644 index 0000000..b6de7c5 --- /dev/null +++ b/561 Array Partition I.py @@ -0,0 +1,22 @@ +#!/usr/bin/python3 +""" +Given an array of 2n integers, your task is to group these integers into n +pairs of integer, say (a1, b1), (a2, b2), ..., (an, bn) which makes sum of +min(ai, bi) for all i from 1 to n as large as possible. + +Example 1: +Input: [1,4,3,2] + +Output: 4 +Explanation: n is 2, and the maximum sum of pairs is 4 = min(1, 2) + min(3, 4). +Note: +n is a positive integer, which is in the range of [1, 10000]. +All the integers in the array will be in the range of [-10000, 10000]. +""" +from typing import List + + +class Solution: + def arrayPairSum(self, nums: List[int]) -> int: + nums.sort() + return sum(nums[::2]) From 22d4035963094985be9650e0371e0ff245662a48 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 9 Feb 2019 16:46:05 -0800 Subject: [PATCH 050/344] 563 --- 563 Binary Tree Tilt.py | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 563 Binary Tree Tilt.py diff --git a/563 Binary Tree Tilt.py b/563 Binary Tree Tilt.py new file mode 100644 index 0000000..dd035ab --- /dev/null +++ b/563 Binary Tree Tilt.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +""" +Given a binary tree, return the tilt of the whole tree. + +The tilt of a tree node is defined as the absolute difference between the sum of +all left subtree node values and the sum of all right subtree node values. Null +node has tilt 0. + +The tilt of the whole tree is defined as the sum of all nodes' tilt. + +Example: +Input: + 1 + / \ + 2 3 +Output: 1 +Explanation: +Tilt of node 2 : 0 +Tilt of node 3 : 0 +Tilt of node 1 : |2-3| = 1 +Tilt of binary tree : 0 + 0 + 1 = 1 +Note: + +The sum of node values in any subtree won't exceed the range of 32-bit integer. +All the tilt values won't exceed the range of 32-bit integer. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def findTilt(self, root: TreeNode) -> int: + ret = [0] + self.walk(root, ret) + return ret[0] + + def walk(self, node: TreeNode, ret) -> int: + if not node: + return 0 + + l = self.walk(node.left, ret) + r = self.walk(node.right, ret) + ret[0] += abs(l - r) + return l + node.val + r From 557f9e8661df76cc7058eb85028f1f5262d5df1e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 10 Feb 2019 15:52:04 -0800 Subject: [PATCH 051/344] 566 --- 563 Binary Tree Tilt.py | 1 + 566 Reshape the Matrix.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 566 Reshape the Matrix.py diff --git a/563 Binary Tree Tilt.py b/563 Binary Tree Tilt.py index dd035ab..1e47a99 100644 --- a/563 Binary Tree Tilt.py +++ b/563 Binary Tree Tilt.py @@ -41,6 +41,7 @@ def findTilt(self, root: TreeNode) -> int: return ret[0] def walk(self, node: TreeNode, ret) -> int: + """get the sum of the subtree and add the tilt""" if not node: return 0 diff --git a/566 Reshape the Matrix.py b/566 Reshape the Matrix.py new file mode 100644 index 0000000..794a058 --- /dev/null +++ b/566 Reshape the Matrix.py @@ -0,0 +1,31 @@ +#!/usr/bin/python3 +""" +In MATLAB, there is a very useful function called 'reshape', which can reshape a +matrix into a new one with different size but keep its original data. + +You're given a matrix represented by a two-dimensional array, and two positive +integers r and c representing the row number and column number of the wanted +reshaped matrix, respectively. + +The reshaped matrix need to be filled with all the elements of the original +matrix in the same row-traversing order as they were. + +If the 'reshape' operation with given parameters is possible and legal, output +the new reshaped matrix; Otherwise, output the original matrix. +""" +from typing import List + +class Solution: + def matrixReshape(self, nums: List[List[int]], r: int, c: int) -> List[List[int]]: + m, n = len(nums), len(nums[0]) + if m * n != r * c: + return nums + + ret = [] + for i in range(m): + for j in range(n): + if (i * n + j) % c == 0: + ret.append([]) + ret[-1].append(nums[i][j]) + + return ret From afff0794b0b03163d8c086ee176076452b276ccf Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 10 Feb 2019 15:59:56 -0800 Subject: [PATCH 052/344] 560 --- 560 Subarray Sum Equals K.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 560 Subarray Sum Equals K.py diff --git a/560 Subarray Sum Equals K.py b/560 Subarray Sum Equals K.py new file mode 100644 index 0000000..3645a93 --- /dev/null +++ b/560 Subarray Sum Equals K.py @@ -0,0 +1,32 @@ +#!/usr/bin/python3 +""" +Given an array of integers and an integer k, you need to find the total +number of continuous subarrays whose sum equals to k. + +Example 1: +Input:nums = [1,1,1], k = 2 +Output: 2 +Note: +The length of the array is in range [1, 20,000]. +The range of numbers in the array is [-1000, 1000] and the range of the integer +k is [-1e7, 1e7]. +""" +from typing import List +from collections import defaultdict + + +class Solution: + def subarraySum(self, nums: List[int], k: int) -> int: + """ + prefix sum + """ + h = defaultdict(int) + ret = 0 + s = 0 + h[s] += 1 + for n in nums: + s += n + ret += h[s - k] + h[s] += 1 + + return ret From 0bb512812ada6db14ca1dca6753d26ae56d25f81 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 10 Feb 2019 19:20:39 -0800 Subject: [PATCH 053/344] 713 --- 713 Subarray Product Less Than K.py | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 713 Subarray Product Less Than K.py diff --git a/713 Subarray Product Less Than K.py b/713 Subarray Product Less Than K.py new file mode 100644 index 0000000..40b8570 --- /dev/null +++ b/713 Subarray Product Less Than K.py @@ -0,0 +1,42 @@ +#!/usr/bin/python3 +""" +Your are given an array of positive integers nums. + +Count and print the number of (contiguous) subarrays where the product of all +the elements in the subarray is less than k. + +Example 1: +Input: nums = [10, 5, 2, 6], k = 100 +Output: 8 +Explanation: The 8 subarrays that have product less than 100 are: [10], [5], +[2], [6], [10, 5], [5, 2], [2, 6], [5, 2, 6]. +Note that [10, 5, 2] is not included as the product of 100 is not strictly less +than k. +Note: + +0 < nums.length <= 50000. +0 < nums[i] < 1000. +0 <= k < 10^6. +""" +from typing import List + + +class Solution: + def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int: + """ + Two pointer + Count attribute to the end + """ + i = 0 + ret = 0 + p = 1 + for j in range(len(nums)): + p *= nums[j] + while p >= k and i <= j: + p //= nums[i] + i += 1 + + ret += j - i + 1 # count attribute + # if i > j, i can only be j + 1 + + return ret From 18070553b99fce061cf85fa1ff6cb80c2e4a9557 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 11 Feb 2019 00:13:48 -0800 Subject: [PATCH 054/344] 554 --- 554 Brick Wall.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 554 Brick Wall.py diff --git a/554 Brick Wall.py b/554 Brick Wall.py new file mode 100644 index 0000000..a1ba03a --- /dev/null +++ b/554 Brick Wall.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +""" +There is a brick wall in front of you. The wall is rectangular and has several +rows of bricks. The bricks have the same height but different width. You want to +draw a vertical line from the top to the bottom and cross the least bricks. + +The brick wall is represented by a list of rows. Each row is a list of integers +representing the width of each brick in this row from left to right. + +If your line go through the edge of a brick, then the brick is not considered as +crossed. You need to find out how to draw the line to cross the least bricks and +return the number of crossed bricks. + +You cannot draw a line just along one of the two vertical edges of the wall, in +which case the line will obviously cross no bricks. + +Example: + +Input: [[1,2,2,1], + [3,1,2], + [1,3,2], + [2,4], + [3,1,2], + [1,3,1,1]] + +Output: 2 + +Explanation: +Note: + +The width sum of bricks in different rows are the same and won't exceed INT_MAX. +The number of bricks in each row is in range [1,10,000]. The height of wall is +in range [1,10,000]. Total number of bricks of the wall won't exceed 20,000. +""" +from typing import List +from collections import defaultdict + + +class Solution: + def leastBricks(self, wall: List[List[int]]) -> int: + """ + Iterate and count edge at a position + """ + h = defaultdict(int) + m = len(wall) + for i in range(m): + s = 0 + for j in range(len(wall[i]) - 1): + # don't count the two endings + s += wall[i][j] + h[s] += 1 + + return m - max(h.values() or [0]) From 67a74219733daaaef8ef9158911ff7124994abc2 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 12 Feb 2019 00:04:24 -0800 Subject: [PATCH 055/344] 565 --- 565 Array Nesting.py | 58 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 565 Array Nesting.py diff --git a/565 Array Nesting.py b/565 Array Nesting.py new file mode 100644 index 0000000..c1a00e2 --- /dev/null +++ b/565 Array Nesting.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 +""" +A zero-indexed array A of length N contains all integers from 0 to N-1. Find and +return the longest length of set S, where S[i] = {A[i], A[A[i]], A[A[A[i]]], ... +} subjected to the rule below. + +Suppose the first element in S starts with the selection of element A[i] of +index = i, the next element in S should be A[A[i]], and then A[A[A[i]]]… By that +analogy, we stop adding right before a duplicate element occurs in S. + + + +Example 1: + +Input: A = [5,4,0,3,1,6,2] +Output: 4 +Explanation: +A[0] = 5, A[1] = 4, A[2] = 0, A[3] = 3, A[4] = 1, A[5] = 6, A[6] = 2. + +One of the longest S[K]: +S[0] = {A[0], A[5], A[6], A[2]} = {5, 6, 2, 0} + + +Note: + +N is an integer within the range [1, 20,000]. +The elements of A are all distinct. +Each element of A is an integer within the range [0, N-1]. +""" +from typing import List + + +class Solution: + def arrayNesting(self, nums: List[int]) -> int: + """ + You can think of it as graph. If circle, then you can start with any + node + """ + visited = set() + ret = 0 + for n in nums: + count = self.dfs(nums, n, set(), visited) + ret = max(ret, count) + + return ret + + def dfs(self, nums, num, path, visited): + if num in visited: + return 0 + + visited.add(num) + path.add(num) # path is subset of visited + self.dfs(nums, nums[num], path, visited) + return len(path) + + +if __name__ == "__main__": + assert Solution().arrayNesting([5,4,0,3,1,6,2]) == 4 From 20c7ce498a5698d2089fc9498f12210dd400ea0f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 13 Feb 2019 00:05:38 -0800 Subject: [PATCH 056/344] 567 --- 567 Permutation in String.py | 52 ++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 567 Permutation in String.py diff --git a/567 Permutation in String.py b/567 Permutation in String.py new file mode 100644 index 0000000..3683442 --- /dev/null +++ b/567 Permutation in String.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 +""" +Given two strings s1 and s2, write a function to return true if s2 contains the +permutation of s1. In other words, one of the first string's permutations is the +substring of the second string. + +Example 1: +Input:s1 = "ab" s2 = "eidbaooo" +Output:True +Explanation: s2 contains one permutation of s1 ("ba"). +Example 2: +Input:s1= "ab" s2 = "eidboaoo" +Output: False +Note: +The input strings only contain lower case letters. +The length of both given strings is in range [1, 10,000]. +""" +from collections import defaultdict + + +class Solution: + def checkInclusion(self, s1: str, s2: str) -> bool: + """ + counter + two pointers + """ + counter = defaultdict(int) + s1_set = set(s1) + for c in s1: + counter[c] += 1 + + i = 0 + j = 0 + while j < len(s2): + if counter[s2[j]] > 0: + counter[s2[j]] -= 1 + if j - i + 1 == len(s1): + return True + j += 1 + else: + if s2[i] in s1_set: + # not check s2[i] in counter, dangerous to check defaultdict + counter[s2[i]] += 1 + i += 1 + if j < i: + j = i + + return False + + +if __name__ == "__main__": + assert Solution().checkInclusion("ab", "eidbaooo") == True + assert Solution().checkInclusion("ab", "eidboaoo") == False From c6c5a3a44b6db5fe5312a808f35380ae970762b0 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 13 Feb 2019 00:58:08 -0800 Subject: [PATCH 057/344] 576 --- 576 Out of Boundary Paths.py | 61 ++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 576 Out of Boundary Paths.py diff --git a/576 Out of Boundary Paths.py b/576 Out of Boundary Paths.py new file mode 100644 index 0000000..237baf7 --- /dev/null +++ b/576 Out of Boundary Paths.py @@ -0,0 +1,61 @@ +#!/usr/bin/python3 +""" +There is an m by n grid with a ball. Given the start coordinate (i,j) of the +ball, you can move the ball to adjacent cell or cross the grid boundary in four +directions (up, down, left, right). However, you can at most move N times. +Find out the number of paths to move the ball out of grid boundary. +The answer may be very large, return it after mod 109 + 7. + +Example 1: + +Input: m = 2, n = 2, N = 2, i = 0, j = 0 +Output: 6 +Explanation: + +Example 2: + +Input: m = 1, n = 3, N = 3, i = 0, j = 1 +Output: 12 +Explanation: + + +Note: + +Once you move the ball out of boundary, you cannot move it back. +The length and height of the grid is in range [1,50]. +N is in range [0,50]. +""" +MOD = 10 ** 9 + 7 +dirs = ((0, 1), (0, -1), (1, 0), (-1, 0)) + + +class Solution: + def findPaths(self, m: int, n: int, N: int, r: int, c: int) -> int: + """ + iterate N epoch + let F[i][j] be the number of paths to reach i, j + F_new[i][j] can be constructed + """ + ret = 0 + F = [[0 for _ in range(n)] for _ in range(m)] + F[r][c] = 1 + for _ in range(N): # epoch + F_new = [[0 for _ in range(n)] for _ in range(m)] + for i in range(m): + for j in range(n): + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < m and 0 <= J < n: + F_new[I][J] = (F_new[I][J] + F[i][j]) % MOD + else: + ret = (ret + F[i][j]) % MOD + + F = F_new + + return ret + + +if __name__ == "__main__": + assert Solution().findPaths(2, 2, 2, 0, 0) == 6 + assert Solution().findPaths(1, 3, 3, 0, 1) == 12 From c4c1628d44dfbe757f4a12021dfaf2c27847a686 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 13 Feb 2019 11:27:56 -0800 Subject: [PATCH 058/344] 581 --- 581 Shortest Unsorted Continuous Subarray.py | 76 ++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 581 Shortest Unsorted Continuous Subarray.py diff --git a/581 Shortest Unsorted Continuous Subarray.py b/581 Shortest Unsorted Continuous Subarray.py new file mode 100644 index 0000000..522ed11 --- /dev/null +++ b/581 Shortest Unsorted Continuous Subarray.py @@ -0,0 +1,76 @@ +#!/usr/bin/python3 +""" +Given an integer array, you need to find one continuous subarray that if you +only sort this subarray in ascending order, then the whole array will be sorted +in ascending order, too. + +You need to find the shortest such subarray and output its length. + +Example 1: +Input: [2, 6, 4, 8, 10, 9, 15] +Output: 5 +Explanation: You need to sort [6, 4, 8, 10, 9] in ascending order to make the +whole array sorted in ascending order. + +Note: +Then length of the input array is in range [1, 10,000]. +The input array may contain duplicates, so ascending order here means <=. +""" +from typing import List + + +class Solution: + def findUnsortedSubarray(self, nums: List[int]) -> int: + """ + Sorted at both ends + Then search for the two ends by nums[i+1] > nums[i] on the left side + (right side similar) + + Problem: may over-include, consider 1 2 5 9 4 6 ... + need to shrink from 1 2 5 9 to 1 2 according to min value + + nums[lo - 1] <= min && max <= nums[hi + 1] + """ + n = len(nums) + lo, hi = 0, n - 1 + while lo < hi and nums[lo] <= nums[lo + 1]: + lo += 1 + + while lo < hi and nums[hi - 1] <= nums[hi]: + hi -= 1 + + if hi <= lo: + return 0 + + mini = float('inf') + maxa = -float('inf') + for i in range(lo, hi + 1): + mini = min(mini, nums[i]) + maxa = max(maxa, nums[i]) + + while lo - 1 >= 0 and nums[lo - 1] > mini: + lo -= 1 + while hi + 1 < n and nums[hi + 1] < maxa: + hi += 1 + + return hi - lo + 1 + + def findUnsortedSubarray_sort(self, nums: List[int]) -> int: + """ + Brute force sort and compare O(n lgn) + """ + expected = list(sorted(nums)) + i = 0 + while i < len(nums) and nums[i] == expected[i]: + i += 1 + + j = len(nums) - 1 + while j >= i and nums[j] == expected[j]: + j -= 1 + + return j - i + 1 + + +if __name__ == "__main__": + assert Solution().findUnsortedSubarray([2, 1]) == 2 + assert Solution().findUnsortedSubarray([2, 6, 4, 8, 10, 9, 15]) == 5 From 7eda5612ca6153c83b6c4d2ac87085d55f71b170 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 13 Feb 2019 11:59:14 -0800 Subject: [PATCH 059/344] 589 590 --- 589 N-ary Tree Preorder Traversal.py | 40 ++++++++++++++++++++++++ 590 N-ary Tree Postorder Traversal.py | 45 +++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 589 N-ary Tree Preorder Traversal.py create mode 100644 590 N-ary Tree Postorder Traversal.py diff --git a/589 N-ary Tree Preorder Traversal.py b/589 N-ary Tree Preorder Traversal.py new file mode 100644 index 0000000..1659cb7 --- /dev/null +++ b/589 N-ary Tree Preorder Traversal.py @@ -0,0 +1,40 @@ +#!/usr/bin/python3 +""" +Given an n-ary tree, return the preorder traversal of its nodes' values. + +For example, given a 3-ary tree: + +Return its preorder traversal as: [1,3,5,6,2,4]. + +Note: +Recursive solution is trivial, could you do it iteratively? +""" + + +# Definition for a Node. +class Node: + def __init__(self, val, children): + self.val = val + self.children = children + + +from typing import List + + +class Solution: + def preorder(self, root: "Node") -> List[int]: + """ + reversely add the children to stk + """ + ret = [] + if not root: + return ret + + stk = [root] + while stk: + cur = stk.pop() + ret.append(cur.val) + for c in reversed(cur.children): + stk.append(c) + + return ret diff --git a/590 N-ary Tree Postorder Traversal.py b/590 N-ary Tree Postorder Traversal.py new file mode 100644 index 0000000..55305a5 --- /dev/null +++ b/590 N-ary Tree Postorder Traversal.py @@ -0,0 +1,45 @@ +#!/usr/bin/python3 +""" +Given an n-ary tree, return the postorder traversal of its nodes' values. + +For example, given a 3-ary tree: + +Return its postorder traversal as: [5,6,3,2,4,1]. + +Note: +Recursive solution is trivial, could you do it iteratively? +""" + + +# Definition for a Node. +class Node: + def __init__(self, val, children): + self.val = val + self.children = children + + +from typing import List + + +class Solution: + def postorder(self, root: 'Node') -> List[int]: + """ + maintain a stack, if no child, then pop + """ + ret = [] + if not root: + return ret + + stk = [root] + visited = set() + while stk: + cur = stk[-1] + if cur in visited: + stk.pop() + ret.append(cur.val) + else: + visited.add(cur) + for c in reversed(cur.children): + stk.append(c) + + return ret From 187e12c2a206f96fdb5458a3bdc6a121233bc1f4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 13 Feb 2019 21:18:41 -0800 Subject: [PATCH 060/344] 590 --- 590 N-ary Tree Postorder Traversal.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/590 N-ary Tree Postorder Traversal.py b/590 N-ary Tree Postorder Traversal.py index 55305a5..f75d850 100644 --- a/590 N-ary Tree Postorder Traversal.py +++ b/590 N-ary Tree Postorder Traversal.py @@ -19,12 +19,31 @@ def __init__(self, val, children): from typing import List +from collections import deque class Solution: def postorder(self, root: 'Node') -> List[int]: """ - maintain a stack, if no child, then pop + maintain a stack, pop and reverse + """ + if not root: + return [] + + ret = deque() + stk = [root] + visited = set() + while stk: + cur = stk.pop() + ret.appendleft(cur.val) + for c in cur.children: + stk.append(c) + + return list(ret) + + def postorder_visited(self, root: 'Node') -> List[int]: + """ + maintain a stack, if visited before, then pop """ ret = [] if not root: From 8f88074f2ebeeec47ad1dd013c525398bfbb7a02 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 13 Feb 2019 22:20:45 -0800 Subject: [PATCH 061/344] 583 --- 583 Delete Operation for Two Strings.py | 78 +++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 583 Delete Operation for Two Strings.py diff --git a/583 Delete Operation for Two Strings.py b/583 Delete Operation for Two Strings.py new file mode 100644 index 0000000..1764248 --- /dev/null +++ b/583 Delete Operation for Two Strings.py @@ -0,0 +1,78 @@ +#!/usr/bin/python3 +""" +Given two words word1 and word2, find the minimum number of steps required to +make word1 and word2 the same, where in each step you can delete one character +in either string. + +Example 1: +Input: "sea", "eat" +Output: 2 +Explanation: You need one step to make "sea" to "ea" and another step to make "eat" to "ea". +Note: +The length of given words won't exceed 500. +Characters in given words can only be lower-case letters. +""" +from collections import defaultdict + + +class Solution: + def minDistance(self, word1: str, word2: str) -> int: + """ + Longest Common Subsequence (LCS) + Find the LCS, and delete the char in BOTH strings into LCS + + Let F[i][j] be length of LCS word1[:i] and word2[:j] + + F[i][j] = F[i-1][j-1] + 1 if word1[i-1] == word2[j-1] + F[i][j] = max(F[i-1][j], F[i][j-1]) + """ + F = defaultdict(lambda: defaultdict(int)) + m = len(word1) + n = len(word2) + + for i in range(1, m + 1): + for j in range(1, n + 1): + if word1[i-1] == word2[j-1]: + F[i][j] = F[i-1][j-1] + 1 + else: + F[i][j] = max( + F[i-1][j], + F[i][j-1], + ) + + return m - F[m][n] + n - F[m][n] + + def minDistance_edit_distance(self, word1: str, word2: str) -> int: + """ + Edit distance + + Let F[i][j] be # operations to make same for word1[:i] and word2[:j] + + F[i][j] = F[i-1][j-1] if word1[i-1] == word2[j-1] + F[i][j] = min(F[i-1][j] + 1, F[i][j-1] + 1) + """ + F = defaultdict(lambda: defaultdict(int)) + m = len(word1) + n = len(word2) + + # initialization is important + for i in range(1, m + 1): + F[i][0] = i + for j in range(1, n + 1): + F[0][j] = j + + for i in range(1, m + 1): + for j in range(1, n + 1): + if word1[i-1] == word2[j-1]: + F[i][j] = F[i-1][j-1] + else: + F[i][j] = min( + F[i-1][j] + 1, + F[i][j-1] + 1, + ) + + return F[m][n] + + +if __name__ == "__main__": + assert Solution().minDistance("sea", "eat") == 2 From 6370060ca0870c208a379372f4c48a313f798251 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 13 Feb 2019 22:36:11 -0800 Subject: [PATCH 062/344] 594 --- 594 Longest Harmonious Subsequence.py | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 594 Longest Harmonious Subsequence.py diff --git a/594 Longest Harmonious Subsequence.py b/594 Longest Harmonious Subsequence.py new file mode 100644 index 0000000..62f2f84 --- /dev/null +++ b/594 Longest Harmonious Subsequence.py @@ -0,0 +1,34 @@ +#!/usr/bin/python3 +""" +We define a harmonious array is an array where the difference between its +maximum value and its minimum value is exactly 1. + +Now, given an integer array, you need to find the length of its longest +harmonious subsequence among all its possible subsequences. + +Example 1: +Input: [1,3,2,2,5,2,3,7] +Output: 5 + +Explanation: The longest harmonious subsequence is [3,2,2,2,3]. +Note: The length of the input array will not exceed 20,000. +""" +from typing import List +from collections import defaultdict + + +class Solution: + def findLHS(self, nums: List[int]) -> int: + """ + counter and iterate + """ + counter = defaultdict(int) + for n in nums: + counter[n] += 1 + + ret = 0 + for k, v in counter.items(): + if k + 1 in counter: + ret = max(ret, v + counter[k + 1]) + + return ret From 0426afd662aab535d0b154c143bee46a32bc3c12 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 13 Feb 2019 22:54:20 -0800 Subject: [PATCH 063/344] 599 --- 599 Minimum Index Sum of Two Lists.py | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 599 Minimum Index Sum of Two Lists.py diff --git a/599 Minimum Index Sum of Two Lists.py b/599 Minimum Index Sum of Two Lists.py new file mode 100644 index 0000000..bfd2fa3 --- /dev/null +++ b/599 Minimum Index Sum of Two Lists.py @@ -0,0 +1,48 @@ +#!/usr/bin/python3 +""" +Suppose Andy and Doris want to choose a restaurant for dinner, and they both +have a list of favorite restaurants represented by strings. + +You need to help them find out their common interest with the least list index +sum. If there is a choice tie between answers, output all of them with no order +requirement. You could assume there always exists an answer. + +Example 1: +Input: +["Shogun", "Tapioca Express", "Burger King", "KFC"] +["Piatti", "The Grill at Torrey Pines", "Hungry Hunter Steakhouse", "Shogun"] +Output: ["Shogun"] +Explanation: The only restaurant they both like is "Shogun". +Example 2: +Input: +["Shogun", "Tapioca Express", "Burger King", "KFC"] +["KFC", "Shogun", "Burger King"] +Output: ["Shogun"] +Explanation: The restaurant they both like and have the least index sum is "Shogun" with index sum 1 (0+1). +Note: +The length of both lists will be in the range of [1, 1000]. +The length of strings in both lists will be in the range of [1, 30]. +The index is starting from 0 to the list length minus 1. +No duplicates in both lists. +""" +from typing import List + + +class Solution: + def findRestaurant(self, list1: List[str], list2: List[str]) -> List[str]: + index = {} + for i, v in enumerate(list2): + index[v] = i + + ret = [] + mini = float('inf') + for i, v in enumerate(list1): + if v in index: + cur = i + index[v] # current index sum + if cur < mini: + mini = cur + ret = [v] + elif cur == mini: + ret.append(v) + + return ret From 0ed44d7da52d5222694108efc274f18b4ca15b66 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 13 Feb 2019 23:24:18 -0800 Subject: [PATCH 064/344] 605 --- 605 Can Place Flowers.py | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 605 Can Place Flowers.py diff --git a/605 Can Place Flowers.py b/605 Can Place Flowers.py new file mode 100644 index 0000000..658816e --- /dev/null +++ b/605 Can Place Flowers.py @@ -0,0 +1,44 @@ +#!/usr/bin/python3 +""" +Suppose you have a long flowerbed in which some of the plots are planted and +some are not. However, flowers cannot be planted in adjacent plots - they would +compete for water and both would die. + +Given a flowerbed (represented as an array containing 0 and 1, where 0 means +empty and 1 means not empty), and a number n, return if n new flowers can be +planted in it without violating the no-adjacent-flowers rule. + +Example 1: +Input: flowerbed = [1,0,0,0,1], n = 1 +Output: True +Example 2: +Input: flowerbed = [1,0,0,0,1], n = 2 +Output: False +Note: +The input array won't violate no-adjacent-flowers rule. +The input array size is in the range of [1, 20000]. +n is a non-negative integer which won't exceed the input array size. +""" +from typing import List + + +class Solution: + def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool: + """ + greedy + """ + if n == 0: + return True + + for i in range(len(flowerbed)): + if ( + flowerbed[i] != 1 and + (i + 1 >= len(flowerbed) or flowerbed[i+1] != 1) and + (i - 1 < 0 or flowerbed[i - 1] != 1) + ): + n -= 1 + flowerbed[i] = 1 + if n == 0: + return True + + return False From 1139689404974ff0e202abbbb4e905554cae48bc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 14 Feb 2019 00:19:33 -0800 Subject: [PATCH 065/344] 611 --- 611 Valid Triangle Number.py | 99 ++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 611 Valid Triangle Number.py diff --git a/611 Valid Triangle Number.py b/611 Valid Triangle Number.py new file mode 100644 index 0000000..a0b9bd5 --- /dev/null +++ b/611 Valid Triangle Number.py @@ -0,0 +1,99 @@ +#!/usr/bin/python3 +""" +Given an array consists of non-negative integers, your task is to count the +number of triplets chosen from the array that can make triangles if we take them +as side lengths of a triangle. + +Example 1: +Input: [2,2,3,4] +Output: 3 +Explanation: +Valid combinations are: +2,3,4 (using the first 2) +2,3,4 (using the second 2) +2,2,3 +Note: +The length of the given array won't exceed 1000. +The integers in the given array are in the range of [0, 1000]. +""" +from typing import List + + +class Solution: + def triangleNumber(self, nums: List[int]) -> int: + """ + b - a < c < a + b + Brute force O(n^3) + + 3 sums + Three-pointers + O(n^2) + """ + ret = 0 + nums.sort() + n = len(nums) + for k in range(n-1, 1, -1): + i = 0 + j = k - 1 + while i < j: + if nums[i] + nums[j] > nums[k]: + ret += j - i # move i will always satisfy the constraint + j -= 1 # to break + else: + i += 1 # to satisfy + + return ret + + def triangleNumber_error(self, nums: List[int]) -> int: + """ + b - a < c < a + b + Brute force O(n^3) + + 3 sums + Three-pointers + O(n^2) + """ + ret = 0 + nums.sort() + n = len(nums) + for i in range(n - 2): + j = i + 1 + k = n - 1 + while j < k: + # error, since move k will not break the formula + if nums[i] + nums[j] > nums[k]: + ret += k - j + k -= 1 + else: + j += 1 + + return ret + + def triangleNumber_slow(self, nums: List[int]) -> int: + """ + b - a < c < a + b + Brute force O(n^3) + + Cache + Prune + """ + cache = {} + nums.sort() + n = len(nums) + ret = 0 + for i in range(n): + for j in range(i + 1, n): + if (i, j) not in cache: + cur = 0 + for k in range(j + 1, n): + if nums[k] < nums[i] + nums[j]: + cur += 1 + else: + break + cache[(i, j)] = cur + ret += cache[(i, j)] + + return ret + + +if __name__ == "__main__": + assert Solution().triangleNumber([2,2,3,4]) == 3 From f5afb56e5faf320d4ad4fb3bba48d202d2d4c8d3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 16 Feb 2019 19:38:11 -0800 Subject: [PATCH 066/344] 623 --- 623 Add One Row to Tree.py | 108 +++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 623 Add One Row to Tree.py diff --git a/623 Add One Row to Tree.py b/623 Add One Row to Tree.py new file mode 100644 index 0000000..bbf4947 --- /dev/null +++ b/623 Add One Row to Tree.py @@ -0,0 +1,108 @@ +#!/usr/bin/python3 +""" +Given the root of a binary tree, then value v and depth d, you need to add a row +of nodes with value v at the given depth d. The root node is at depth 1. + +The adding rule is: given a positive integer depth d, for each NOT null tree +nodes N in depth d-1, create two tree nodes with value v as N's left subtree +root and right subtree root. And N's original left subtree should be the left +subtree of the new left subtree root, its original right subtree should be the +right subtree of the new right subtree root. If depth d is 1 that means there is +no depth d-1 at all, then create a tree node with value v as the new root of the +whole original tree, and the original tree is the new root's left subtree. + +Example 1: +Input: +A binary tree as following: + 4 + / \ + 2 6 + / \ / + 3 1 5 + +v = 1 + +d = 2 + +Output: + 4 + / \ + 1 1 + / \ + 2 6 + / \ / + 3 1 5 + +Example 2: +Input: +A binary tree as following: + 4 + / + 2 + / \ + 3 1 + +v = 1 + +d = 3 + +Output: + 4 + / + 2 + / \ + 1 1 + / \ +3 1 +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def addOneRow(self, root: TreeNode, v: int, d: int) -> TreeNode: + return self.add(root, v, d, 1, "left") + + def add(self, node, v, d, cur_d, child) -> TreeNode: + # use the return value for parent's reference + if cur_d == d: + new = TreeNode(v) + setattr(new, child, node) + return new + + if node: + node.left = self.add(node.left, v, d, cur_d + 1, "left") + node.right = self.add(node.right, v, d, cur_d + 1, "right") + return node + + +class Solution2: + def addOneRow(self, root: TreeNode, v: int, d: int) -> TreeNode: + if d == 1: + node = TreeNode(v) + node.left = root + return node + + self.add(self, root, v, d, 1) + return root + + def add(self, node, v, d, cur_d) -> None: + if not node: + return + + if cur_d + 1 == d: + left = node.left + right = node.right + node.left = TreeNode(v) + node.left.left = left + node.right = TreeNode(v) + node.right.right = right + + self.add(node.left, v, d, cur_d + 1) + self.add(node.right, v, d, cur_d + 1) From 2e434c54d29469fb10bd9bc7eb948fbe3387afcc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 16 Feb 2019 20:01:56 -0800 Subject: [PATCH 067/344] 617 --- 617 Merge Two Binary Trees.py | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 617 Merge Two Binary Trees.py diff --git a/617 Merge Two Binary Trees.py b/617 Merge Two Binary Trees.py new file mode 100644 index 0000000..28adb7a --- /dev/null +++ b/617 Merge Two Binary Trees.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +""" +Given two binary trees and imagine that when you put one of them to cover the +other, some nodes of the two trees are overlapped while the others are not. + +You need to merge them into a new binary tree. The merge rule is that if two +nodes overlap, then sum node values up as the new value of the merged node. +Otherwise, the NOT null node will be used as the node of new tree. + +Example 1: + +Input: + Tree 1 Tree 2 + 1 2 + / \ / \ + 3 2 1 3 + / \ \ + 5 4 7 +Output: +Merged tree: + 3 + / \ + 4 5 + / \ \ + 5 4 7 + + +Note: The merging process must start from the root nodes of both trees. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode: + if not t1 and not t2: + return + + node = TreeNode(0) + node.val += t1.val if t1 else 0 + node.val += t2.val if t2 else 0 + node.left = self.mergeTrees(t1 and t1.left, t2 and t2.left) + node.right = self.mergeTrees(t1 and t1.right, t2 and t2.right) + return node From 304016e67537acbb5fdfcf0d03ecf1c600549dbd Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 16 Feb 2019 20:11:05 -0800 Subject: [PATCH 068/344] or --- 617 Merge Two Binary Trees.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/617 Merge Two Binary Trees.py b/617 Merge Two Binary Trees.py index 28adb7a..2d633f5 100644 --- a/617 Merge Two Binary Trees.py +++ b/617 Merge Two Binary Trees.py @@ -41,10 +41,10 @@ class Solution: def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode: if not t1 and not t2: return - + node = TreeNode(0) - node.val += t1.val if t1 else 0 - node.val += t2.val if t2 else 0 + node.val += t1 and t1.val or 0 + node.val += t2 and t2.val or 0 node.left = self.mergeTrees(t1 and t1.left, t2 and t2.left) node.right = self.mergeTrees(t1 and t1.right, t2 and t2.right) return node From d3358ac26531ac5fea7da8f66dfc2cd9634852f0 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 16 Feb 2019 21:14:30 -0800 Subject: [PATCH 069/344] 621 --- 621 Task Scheduler.py | 101 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 621 Task Scheduler.py diff --git a/621 Task Scheduler.py b/621 Task Scheduler.py new file mode 100644 index 0000000..226cf55 --- /dev/null +++ b/621 Task Scheduler.py @@ -0,0 +1,101 @@ +#!/usr/bin/python3 +""" +Given a char array representing tasks CPU need to do. It contains capital +letters A to Z where different letters represent different tasks. Tasks could be +done without original order. Each task could be done in one interval. For each +interval, CPU could finish one task or just be idle. + +However, there is a non-negative cooling interval n that means between two same +tasks, there must be at least n intervals that CPU are doing different tasks or +just be idle. + +You need to return the least number of intervals the CPU will take to finish all +the given tasks. + +Example: + +Input: tasks = ["A","A","A","B","B","B"], n = 2 +Output: 8 +Explanation: A -> B -> idle -> A -> B -> idle -> A -> B. + +Note: + +The number of tasks is in the range [1, 10000]. +The integer n is in the range [0, 100]. +""" +from typing import List +from collections import deque, defaultdict +import heapq + + +class Solution: + def leastInterval(self, tasks: List[str], n: int) -> int: + """ + Gap is n + + Find the idle count + + use the max letter to construct page, # of page is max - 1 + (need also consider duplicate) + + Each page size is n + 1 + Free page size is n + 1 - (# of max) + Find the idle count + """ + counter = defaultdict(int) + for t in tasks: + counter[t] += 1 + + maxa = 0 + max_cnt = 0 + for v in counter.values(): + if v > maxa: + maxa = v + max_cnt = 1 + elif v == maxa: + max_cnt += 1 + + page_cnt = maxa - 1 + free_page_size = n + 1 - max_cnt + small_tasks = len(tasks) - max_cnt * maxa + idle = max(0, page_cnt * free_page_size - small_tasks) + return len(tasks) + idle + + + def leastInterval_complicated(self, tasks: List[str], n: int) -> int: + """ + greedy + max heap, most tasks first + cool down queue + """ + counter = defaultdict(int) + for t in tasks: + counter[t] += 1 + + pq = [ + (-v, k) + for k, v in counter.items() + ] + heapq.heapify(pq) + q = deque() # stores (t, k) + clock = 0 + while pq or q: + if q and q[0][0] <= clock: + # don't do while in while when clock++ + _, k = q.popleft() + heapq.heappush(pq, (-counter[k], k)) + + if pq: + _, k = heapq.heappop(pq) + counter[k] -= 1 + if counter[k] > 0: + q.append((clock + 1 + n, k)) + + clock += 1 + + return clock + + +if __name__ == "__main__": + assert Solution().leastInterval(["A","A","A","B","B","B"], 0) == 6 + assert Solution().leastInterval(["A","A","A","B","B","B"], 2) == 8 From 96e4e0e28b05db95ce25d0ac98e642ae4a409836 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 16 Feb 2019 22:03:51 -0800 Subject: [PATCH 070/344] 670 --- 670 Maximum Swap.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 670 Maximum Swap.py diff --git a/670 Maximum Swap.py b/670 Maximum Swap.py new file mode 100644 index 0000000..50c66f8 --- /dev/null +++ b/670 Maximum Swap.py @@ -0,0 +1,45 @@ +#!/usr/bin/python3 +""" +Given a non-negative integer, you could swap two digits at most once to get the +maximum valued number. Return the maximum valued number you could get. + +Example 1: +Input: 2736 +Output: 7236 +Explanation: Swap the number 2 and the number 7. +Example 2: +Input: 9973 +Output: 9973 +Explanation: No swap. +Note: +The given number is in the range [0, 108] +""" + + +class Solution: + def maximumSwap(self, num: int) -> int: + """ + stk maintain a increasing stack from right to left + """ + stk = [] + nums = list(str(num)) + n = len(nums) + for i in range(n-1, -1, -1): + if stk and stk[-1][1] >= nums[i]: # only keep the rightmost duplicate + continue + stk.append((i, nums[i])) + + for i in range(n): + while stk and stk[-1][0] <= i: + stk.pop() + if stk and stk[-1][1] > nums[i]: + j = stk[-1][0] + nums[i], nums[j] = nums[j], nums[i] + break + + return int("".join(nums)) + + +if __name__ == "__main__": + assert Solution().maximumSwap(2736) == 7236 + assert Solution().maximumSwap(9973) == 9973 From 269c6289c35b995bcf2fbbff6754fa30e87f148a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Feb 2019 00:22:53 -0800 Subject: [PATCH 071/344] 674 --- ...ngest Continuous Increasing Subsequence.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 674 Longest Continuous Increasing Subsequence.py diff --git a/674 Longest Continuous Increasing Subsequence.py b/674 Longest Continuous Increasing Subsequence.py new file mode 100644 index 0000000..248f245 --- /dev/null +++ b/674 Longest Continuous Increasing Subsequence.py @@ -0,0 +1,42 @@ +#!/usr/bin/python3 +""" +Given an unsorted array of integers, find the length of longest continuous +increasing subsequence (subarray). + +Example 1: +Input: [1,3,5,4,7] +Output: 3 +Explanation: The longest continuous increasing subsequence is [1,3,5], its +length is 3. +Even though [1,3,5,7] is also an increasing subsequence, it's not a continuous +one where 5 and 7 are separated by 4. +Example 2: +Input: [2,2,2,2,2] +Output: 1 +Explanation: The longest continuous increasing subsequence is [2], its length +is 1. +Note: Length of the array will not exceed 10,000. +""" +from typing import List + + +class Solution: + def findLengthOfLCIS(self, nums: List[int]) -> int: + """ + pointer is sufficient + """ + if not nums: + return 0 + + ret = 1 + i = 1 + while i < len(nums): + cur = 1 + while i < len(nums) and nums[i] > nums[i-1]: + cur += 1 + i += 1 + + i += 1 + ret = max(ret, cur) + + return ret From 0e4840196add1c5bd2d16b0f3d343e2c6cfd64be Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Feb 2019 15:06:36 -0800 Subject: [PATCH 072/344] 680 --- 680 Valid Palindrome II.py | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 680 Valid Palindrome II.py diff --git a/680 Valid Palindrome II.py b/680 Valid Palindrome II.py new file mode 100644 index 0000000..d2f24fd --- /dev/null +++ b/680 Valid Palindrome II.py @@ -0,0 +1,47 @@ +#!/usr/bin/python3 +""" +Given a non-empty string s, you may delete at most one character. Judge whether +you can make it a palindrome. + +Example 1: +Input: "aba" +Output: True +Example 2: +Input: "abca" +Output: True +Explanation: You could delete the character 'c'. +Note: +The string will only contain lowercase characters a-z. The maximum length of the +string is 50000. +""" + + +class Solution: + def validPalindrome(self, s: str) -> bool: + """ + Brute force, delete and check. O(n^2) + + Start from start and end, then check equal. If not match, skip either + side (i.e. delete a character), then check palindrome + """ + n = len(s) + i = 0 + j = n - 1 + while i < j: + if s[i] == s[j]: + i += 1 + j -= 1 + else: + # error, for -1, start > end. Indexing is like range + # return s[i:j] == s[i:j:-1] or s[i+1:j+1] == s[i+1:j+1:-1] + return self.is_palindrome(s[i:j]) or self.is_palindrome(s[i+1:j+1]) + + return True + + def is_palindrome(self, s): + return s == s[::-1] + + +if __name__ == "__main__": + assert Solution().validPalindrome("aba") == True + assert Solution().validPalindrome("abca") == True From 0c5f45c8c777060eb1e33f6cf1146610fe562727 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Feb 2019 16:41:37 -0800 Subject: [PATCH 073/344] 673 --- ...umber of Longest Increasing Subsequence.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 673 Number of Longest Increasing Subsequence.py diff --git a/673 Number of Longest Increasing Subsequence.py b/673 Number of Longest Increasing Subsequence.py new file mode 100644 index 0000000..67a3b72 --- /dev/null +++ b/673 Number of Longest Increasing Subsequence.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 +""" +Given an unsorted array of integers, find the number of longest increasing +subsequence. + +Example 1: +Input: [1,3,5,4,7] +Output: 2 +Explanation: The two longest increasing subsequence are [1, 3, 4, 7] and +[1, 3, 5, 7]. +Example 2: +Input: [2,2,2,2,2] +Output: 5 +Explanation: The length of longest continuous increasing subsequence is 1, and +there are 5 subsequences' length is 1, so output 5. +Note: Length of the given array will be not exceed 2000 and the answer is +guaranteed to be fit in 32-bit signed int. +""" +from typing import List + + +class LenCnt: + def __init__(self, l, c): + self.l = l + self.c = c + + def __repr__(self): + return repr((self.l, self.c)) + + +class Solution: + def findNumberOfLIS(self, A: List[int]) -> int: + """ + Two pass - 1st pass find the LIS, 2nd pass find the number + Let F[i] be the length of LIS ended at A[i] + """ + if not A: + return 0 + + n = len(A) + F = [LenCnt(l=1, c=1) for _ in A] + mx = LenCnt(l=1, c=1) + for i in range(1, n): + for j in range(i): + if A[i] > A[j]: + if F[i].l < F[j].l + 1: + F[i].l = F[j].l + 1 + F[i].c = F[j].c + elif F[i].l == F[j].l + 1: + F[i].c += F[j].c + + if F[i].l > mx.l: + # mx = F[i] error, need deep copy + mx.l = F[i].l + mx.c = F[i].c + elif F[i].l == mx.l: + mx.c += F[i].c + + return mx.c + + +if __name__ == "__main__": + assert Solution().findNumberOfLIS([1,1,1,2,2,2,3,3,3]) == 27 + assert Solution().findNumberOfLIS([1, 3, 5, 4, 7]) == 2 + assert Solution().findNumberOfLIS([2, 2, 2, 2, 2]) == 5 From e3e57df22057a7a11cd2a2d8b824a8437a65088c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Feb 2019 19:22:53 -0800 Subject: [PATCH 074/344] 684 --- 678 Valid Parenthesis String.py | 61 +++++++++++++ 684 Redundant Connection.py | 146 ++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 678 Valid Parenthesis String.py create mode 100644 684 Redundant Connection.py diff --git a/678 Valid Parenthesis String.py b/678 Valid Parenthesis String.py new file mode 100644 index 0000000..555c47a --- /dev/null +++ b/678 Valid Parenthesis String.py @@ -0,0 +1,61 @@ +#!/usr/bin/python3 +""" +Given a string containing only three types of characters: '(', ')' and '*', +write a function to check whether this string is valid. We define the validity +of a string by these rules: + +Any left parenthesis '(' must have a corresponding right parenthesis ')'. +Any right parenthesis ')' must have a corresponding left parenthesis '('. +Left parenthesis '(' must go before the corresponding right parenthesis ')'. +'*' could be treated as a single right parenthesis ')' or a single left +parenthesis '(' or an empty string. +An empty string is also valid. + +Example 1: +Input: "()" +Output: True +Example 2: +Input: "(*)" +Output: True +Example 3: +Input: "(*))" +Output: True +Note: +The string size will be in the range [1, 100]. +""" + + +class Solution: + def checkValidString(self, s: str) -> bool: + """ + Brute force: dfs branching on "*". + + Better Solution: + keep two stack: stak of "(" and stack of "*" + """ + stk_left = [] + stk_star = [] + for i, c in enumerate(s): + if c == "(": + stk_left.append(i) + elif c == "*": + stk_star.append(i) + else: + if stk_left: + stk_left.pop() + elif stk_star: + stk_star.pop() + else: + return False + + while stk_left and stk_star and stk_star[-1] > stk_left[-1]: + stk_star.pop() + stk_left.pop() + + return not stk_left + + +if __name__ == "__main__": + assert Solution().checkValidString("(*))") == True + assert Solution().checkValidString("*(") == False + assert Solution().checkValidString("(*)") == True diff --git a/684 Redundant Connection.py b/684 Redundant Connection.py new file mode 100644 index 0000000..c75e197 --- /dev/null +++ b/684 Redundant Connection.py @@ -0,0 +1,146 @@ +#!/usr/bin/python3 +""" +In this problem, a tree is an undirected graph that is connected and has no +cycles. + +The given input is a graph that started as a tree with N nodes (with distinct +values 1, 2, ..., N), with one additional edge added. The added edge has two +different vertices chosen from 1 to N, and was not an edge that already existed. + +The resulting graph is given as a 2D-array of edges. Each element of edges is a +pair [u, v] with u < v, that represents an undirected edge connecting nodes u +and v. + +Return an edge that can be removed so that the resulting graph is a tree of N +nodes. If there are multiple answers, return the answer that occurs last in the given 2D-array. The answer edge [u, v] should be in the same format, with u < v. + +Example 1: +Input: [[1,2], [1,3], [2,3]] +Output: [2,3] +Explanation: The given undirected graph will be like this: + 1 + / \ +2 - 3 +Example 2: +Input: [[1,2], [2,3], [3,4], [1,4], [1,5]] +Output: [1,4] +Explanation: The given undirected graph will be like this: +5 - 1 - 2 + | | + 4 - 3 +Note: +The size of the input 2D-array will be between 3 and 1000. +Every integer represented in the 2D-array will be between 1 and N, where N is +the size of the input array. +""" +from typing import List +from collections import defaultdict + + +class DisjointSet(): + def __init__(self): + self.sz = {} # element -> size + self.pi = {} # element -> pi + + def add(self, x): + if x not in self.pi: # need to check, otherwise override wrongly + self.sz[x] = 1 + self.pi[x] = x + + def unionize(self, x, y): + p1 = self.root(x) + p2 = self.root(y) + if p1 != p2: + sz1 = self.sz[p1] + sz2 = self.sz[p2] + if sz1 > sz2: + p1, p2 = p2, p1 + + self.pi[p1] = p2 + self.sz[p2] += self.sz[p1] + del self.sz[p1] + + def root(self, x): + p = self.pi[x] + if p != x: + self.pi[x] = self.root(p) + + return self.pi[x] + + def is_union(self, x, y): + if x in self.pi and y in self.pi: + return self.root(x) == self.root(y) + + return False + + +class Solution: + def findRedundantConnection(self, edges: List[List[int]]) -> List[int]: + """ + Union-find + """ + ds = DisjointSet() + for p, q in edges: + ds.add(p) + ds.add(q) + if ds.is_union(p, q): + return [p, q] + + ds.unionize(p, q) + + raise + +class Solution_dfs: + def findRedundantConnection(self, edges: List[List[int]]) -> List[int]: + """ + Construct graph: O(|E|) + Find circle through dfs: O(|V|) + Notice: need to extract the circle from the cyclic path + """ + G = defaultdict(set) + for p, q in edges: + G[p].add(q) + G[q].add(p) + + visited = set() + for k in G.keys(): + if k not in visited: + circle = self.dfs(G, k, None, set([k]), [k], visited) + if circle: + for p, q in reversed(edges): + if p in circle and q in circle: + return [p, q] + + raise + + def dfs(self, G, cur, pi, path, path_list, visited): + visited.add(cur) + + for nbr in G[cur]: + if nbr != pi: + if nbr in path: + # extract the circle from path + circle = set() + in_circle = False + for e in path_list: + if e == nbr: + in_circle = True + if in_circle: + circle.add(e) + return circle + + path.add(nbr) + path_list.append(nbr) + circle = self.dfs(G, nbr, cur, path, path_list, visited) + if circle: + return circle + path.remove(nbr) + path_list.pop() + + return None + + +if __name__ == "__main__": + assert Solution().findRedundantConnection([[1,2], [1,3], [2,3]]) == [2, 3] + assert Solution().findRedundantConnection([[1,2], [2,3], [3,4], [1,4], [1,5]]) == [1, 4] + assert Solution().findRedundantConnection([[30,44],[34,47],[22,32],[35,44],[26,36],[2,15],[38,41],[28,35],[24,37],[14,49],[44,45],[11,50],[20,39],[7,39],[19,22],[3,17],[15,25],[1,39],[26,40],[5,14],[6,23],[5,6],[31,48],[13,22],[41,44],[10,19],[12,41],[1,12],[3,14],[40,50],[19,37],[16,26],[7,25],[22,33],[21,27],[9,50],[24,42],[43,46],[21,47],[29,40],[31,34],[9,31],[14,31],[5,48],[3,18],[4,19],[8,17],[38,46],[35,37],[17,43]]) == [5,48] From 07facc0e2122513d28faebd2b3cde182bcf041ef Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Feb 2019 22:31:58 -0800 Subject: [PATCH 075/344] 679 --- 679 24 Game.py | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 679 24 Game.py diff --git a/679 24 Game.py b/679 24 Game.py new file mode 100644 index 0000000..2c6803e --- /dev/null +++ b/679 24 Game.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +""" +You have 4 cards each containing a number from 1 to 9. You need to judge whether +they could operated through *, /, +, -, (, ) to get the value of 24. + +Example 1: +Input: [4, 1, 8, 7] +Output: True +Explanation: (8-4) * (7-1) = 24 +Example 2: +Input: [1, 2, 1, 2] +Output: False +Note: +The division operator / represents real division, not integer division. For +example, 4 / (1 - 2/3) = 12. +Every operation done is between two numbers. In particular, we cannot use - as a +unary operator. For example, with [1, 1, 1, 1] as input, the expression -1 - 1 - 1 - 1 is not allowed. +You cannot concatenate numbers together. For example, if the input is +[1, 2, 1, 2], we cannot write this as 12 + 12. +""" +from typing import List + + +class Solution: + def judgePoint24(self, nums: List[int]) -> bool: + return self.dfs(nums, {}) + + def dfs(self, A, cache): + if tuple(A) not in cache: + n = len(A) + if n == 1: + return abs(A[0] - 24) < 0.001 + + for i in range(n): + for j in range(i): + a = A[i] + b = A[j] + for c in (a+b, a-b, b-a, a*b, b and a/b, a and b/a): + # if 0, duplicated as a * b + A_new = A[:j] + A[j+1:i] + A[i+1:] + [c] + A_new.sort() + if self.dfs(A_new, cache): + cache[tuple(A)] = True + return cache[tuple(A)] + + cache[tuple(A)] = False + + return cache[tuple(A)] + + +if __name__ == "__main__": + assert Solution().judgePoint24([4, 1, 8, 7]) == True + assert Solution().judgePoint24([1, 2, 1, 2]) == False From 81fca87fc62e771f1762b2e1972632f9bede18c7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Feb 2019 23:21:35 -0800 Subject: [PATCH 076/344] 677 --- 677 Map Sum Pairs.py | 127 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 677 Map Sum Pairs.py diff --git a/677 Map Sum Pairs.py b/677 Map Sum Pairs.py new file mode 100644 index 0000000..91e9544 --- /dev/null +++ b/677 Map Sum Pairs.py @@ -0,0 +1,127 @@ +#!/usr/bin/python3 +""" +Implement a MapSum class with insert, and sum methods. + +For the method insert, you'll be given a pair of (string, integer). The string +represents the key and the integer represents the value. If the key already +existed, then the original key-value pair will be overridden to the new one. + +For the method sum, you'll be given a string representing the prefix, and you +need to return the sum of all the pairs' value whose key starts with the prefix. + +Example 1: +Input: insert("apple", 3), Output: Null +Input: sum("ap"), Output: 3 +Input: insert("app", 2), Output: Null +Input: sum("ap"), Output: 5 +""" + + +class MapSum: + + def __init__(self): + """ + Initialize your data structure here. + + Trie + + update using delta + """ + from collections import defaultdict + + class TrieNode: + def __init__(self, chr, sum, val): + self.chr = chr + self.sum = sum + self.val = val + self.children = defaultdict(lambda: None) + + class Trie: + def __init__(self): + self.root = TrieNode(None, 0, 0) # dummy root + + def insert(self, cur, key, i, val): + if not cur: + cur = TrieNode(key[i], 0, 0) + + if i == len(key) - 1: + delta = val - cur.val + cur.val = val + else: + cur.children[key[i+1]], delta = self.insert(cur.children[key[i+1]], key, i + 1, val) + + cur.sum += delta + return cur, delta + + self.trie = Trie() + + def insert(self, key: str, val: int) -> None: + self.trie.root.children[key[0]], _ = self.trie.insert(self.trie.root.children[key[0]], key, 0, val) + + def sum(self, prefix: str) -> int: + node = self.trie.root + for a in prefix: + if a not in node.children: + return 0 + + node = node.children[a] + + return node.sum + + +class MapSum2: + + def __init__(self): + """ + Initialize your data structure here. + + Trie + + update using delta + """ + class TrieNode: + def __init__(self, chr, sum, val): + self.chr = chr + self.sum = sum + self.val = val + self.children = {} + + class Trie: + def __init__(self): + self.root = TrieNode(None, 0, 0) # dummy root + + def insert(self, pi, key, i, val): + if key[i] not in pi.children: + cur = TrieNode(key[i], 0, 0) + pi.children[key[i]] = cur + + cur = pi.children[key[i]] + if i + 1 < len(key): + cur.children[key[i+1]], delta = self.insert(cur, key, i + 1, val) + else: + delta = val - cur.val + cur.val = val + + cur.sum += delta + return cur, delta + + self.trie = Trie() + + def insert(self, key: str, val: int) -> None: + self.trie.insert(self.trie.root, key, 0, val) + + def sum(self, prefix: str) -> int: + node = self.trie.root + for a in prefix: + if a not in node.children: + return 0 + + node = node.children[a] + + return node.sum + + +# Your MapSum object will be instantiated and called as such: +# obj = MapSum() +# obj.insert(key,val) +# param_2 = obj.sum(prefix) From a9d399efd8181343b373dc40e8091177bbd0b35a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Feb 2019 23:24:00 -0800 Subject: [PATCH 077/344] root --- 677 Map Sum Pairs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/677 Map Sum Pairs.py b/677 Map Sum Pairs.py index 91e9544..19eb889 100644 --- a/677 Map Sum Pairs.py +++ b/677 Map Sum Pairs.py @@ -56,7 +56,8 @@ def insert(self, cur, key, i, val): self.trie = Trie() def insert(self, key: str, val: int) -> None: - self.trie.root.children[key[0]], _ = self.trie.insert(self.trie.root.children[key[0]], key, 0, val) + root = self.trie.root + root.children[key[0]], _ = self.trie.insert(root.children[key[0]], key, 0, val) def sum(self, prefix: str) -> int: node = self.trie.root From aa547b4b42def5a61cb97d44fb21285ce8e74992 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 18 Feb 2019 01:00:25 -0800 Subject: [PATCH 078/344] 676 --- 676 Implement Magic Dictionary.py | 90 +++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 676 Implement Magic Dictionary.py diff --git a/676 Implement Magic Dictionary.py b/676 Implement Magic Dictionary.py new file mode 100644 index 0000000..d546ed9 --- /dev/null +++ b/676 Implement Magic Dictionary.py @@ -0,0 +1,90 @@ +#!/usr/bin/python3 +""" +Implement a magic directory with buildDict, and search methods. + +For the method buildDict, you'll be given a list of non-repetitive words to +build a dictionary. + +For the method search, you'll be given a word, and judge whether if you modify +exactly one character into another character in this word, the modified word is in the dictionary you just built. + +Example 1: +Input: buildDict(["hello", "leetcode"]), Output: Null +Input: search("hello"), Output: False +Input: search("hhllo"), Output: True +Input: search("hell"), Output: False +Input: search("leetcoded"), Output: False +""" +from typing import List +from collections import defaultdict + + +class MagicDictionary: + + def __init__(self): + """ + Initialize your data structure here. + """ + class Node: + def __init__(self, chr): + self.chr = chr + self.end = False # a word ends here + self.children = defaultdict(lambda: None) + + class Trie: + def __init__(self): + self.root = Node(None) + + def insert(self, cur, s, i): + if not cur: + cur = Node(s[i]) + + if i == len(s) -1: + cur.end = True + else: + nxt = s[i+1] + cur.children[nxt] = self.insert(cur.children[nxt], s, i + 1) + + return cur + + def search(self, cur, s, i, modified): + if cur.chr != s[i]: + if modified: + return False + modified = True + + if i == len(s) - 1: + # modified exactly once and have a word ends here + return modified and cur.end + + for child in cur.children.values(): + if self.search(child, s, i + 1, modified): + return True + + return False + + self.trie = Trie() + + def buildDict(self, dic: List[str]) -> None: + """ + Build a dictionary through a list of words + """ + for s in dic: + root = self.trie.root + root.children[s[0]] = self.trie.insert(root.children[s[0]], s, 0) + + def search(self, word: str) -> bool: + """ + Returns if there is any word in the trie that equals to the given word after modifying exactly one character + """ + for child in self.trie.root.children.values(): + if self.trie.search(child, word, 0, False): + return True + + return False + + +# Your MagicDictionary object will be instantiated and called as such: +# obj = MagicDictionary() +# obj.buildDict(dict) +# param_2 = obj.search(word) From a68a34d45b94d928eefe62a1c19059a447f54952 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 18 Feb 2019 13:58:40 -0800 Subject: [PATCH 079/344] 654, 662 --- 654 Maximum Binary Tree.py | 97 +++++++++++++++++++++++++++++ 662 Maximum Width of Binary Tree.py | 97 +++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 654 Maximum Binary Tree.py create mode 100644 662 Maximum Width of Binary Tree.py diff --git a/654 Maximum Binary Tree.py b/654 Maximum Binary Tree.py new file mode 100644 index 0000000..6ab869a --- /dev/null +++ b/654 Maximum Binary Tree.py @@ -0,0 +1,97 @@ +#!/usr/bin/python3 +""" +Given an integer array with no duplicates. A maximum tree building on this array +is defined as follow: + +The root is the maximum number in the array. +The left subtree is the maximum tree constructed from left part subarray divided +by the maximum number. +The right subtree is the maximum tree constructed from right part subarray +divided by the maximum number. +Construct the maximum tree by the given array and output the root node of this +tree. + +Example 1: +Input: [3,2,1,6,0,5] +Output: return the tree root node representing the following tree: + + 6 + / \ + 3 5 + \ / + 2 0 + \ + 1 +Note: +The size of the given array will be in the range [1,1000]. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +from typing import List +import heapq + + +class Solution: + def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode: + """ + monotonic stack - a stack to keep a decreasing subsequence from left to + right + the cur is the stk[-1]'s right + the cur's left is elements to its left not in monotonic stack + """ + stk = [] + for n in nums: + cur = TreeNode(n) + while stk and stk[-1].val < cur.val: + left = stk.pop() + cur.left = left + + if stk: + stk[-1].right = cur + + stk.append(cur) + + return stk[0] + +class Solution_heap: + def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode: + """ + heap O(n lgn) + insert by index O(n lgn) + """ + if not nums: + return + + h = [(-v, v) for v in nums] + idx = { + v: i + for i, v in enumerate(nums) + } + heapq.heapify(h) + root = None + while h: + _, m = heapq.heappop(h) + root = self.insert(root, m, idx) + + return root + + def insert(self, node, m, idx): + if not node: + return TreeNode(m) + + if idx[m] < idx[node.val]: + node.left = self.insert(node.left, m, idx) + elif idx[m] > idx[node.val]: + node.right = self.insert(node.right, m, idx) + else: + raise + + return node diff --git a/662 Maximum Width of Binary Tree.py b/662 Maximum Width of Binary Tree.py new file mode 100644 index 0000000..69512c2 --- /dev/null +++ b/662 Maximum Width of Binary Tree.py @@ -0,0 +1,97 @@ +#!/usr/bin/python3 +""" +Given a binary tree, write a function to get the maximum width of the given +tree. The width of a tree is the maximum width among all levels. The binary tree +has the same structure as a full binary tree, but some nodes are null. + +The width of one level is defined as the length between the end-nodes (the +leftmost and right most non-null nodes in the level, where the null nodes +between the end-nodes are also counted into the length calculation. + +Example 1: + +Input: + + 1 + / \ + 3 2 + / \ \ + 5 3 9 + +Output: 4 +Explanation: The maximum width existing in the third level with the length 4 (5,3,null,9). +Example 2: + +Input: + + 1 + / + 3 + / \ + 5 3 + +Output: 2 +Explanation: The maximum width existing in the third level with the length 2 (5,3). +Example 3: + +Input: + + 1 + / \ + 3 2 + / + 5 + +Output: 2 +Explanation: The maximum width existing in the second level with the length 2 (3,2). +Example 4: + +Input: + + 1 + / \ + 3 2 + / \ + 5 9 + / \ + 6 7 +Output: 8 +Explanation:The maximum width existing in the fourth level with the length 8 (6,null,null,null,null,null,null,7). +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def widthOfBinaryTree(self, root: TreeNode) -> int: + """ + 0 + 0 1 + 0 1 2 3 + + BFS, level index + """ + if not root: + return 0 + + ret = 0 + q = [(0, root)] # (index, node) + while q: + cur_q = [] + left, right = q[0][0], q[-1][0] + ret = max(ret, right - left + 1) + for idx, node in q: + if node.left: + cur_q.append((idx * 2, node.left)) + if node.right: + cur_q.append((idx * 2 + 1, node.right)) + + q = cur_q + + return ret From 109d38329715285c1bfd2ab103750d0be7ac6b47 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 18 Feb 2019 14:22:47 -0800 Subject: [PATCH 080/344] 648 --- 648 Replace Words.py | 93 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 648 Replace Words.py diff --git a/648 Replace Words.py b/648 Replace Words.py new file mode 100644 index 0000000..95682aa --- /dev/null +++ b/648 Replace Words.py @@ -0,0 +1,93 @@ +#!/usr/bin/python3 +""" +In English, we have a concept called root, which can be followed by some other +words to form another longer word - let's call this word successor. For example, +the root an, followed by other, which can form another word another. + +Now, given a dictionary consisting of many roots and a sentence. You need to +replace all the successor in the sentence with the root forming it. If a +successor has many roots can form it, replace it with the root with the shortest +length. + +You need to output the sentence after the replacement. + +Example 1: + +Input: dict = ["cat", "bat", "rat"] +sentence = "the cattle was rattled by the battery" +Output: "the cat was rat by the bat" + + +Note: + +The input will only have lower-case letters. +1 <= dict words number <= 1000 +1 <= sentence words number <= 1000 +1 <= root length <= 100 +1 <= sentence words length <= 1000 +""" +from typing import List +from collections import defaultdict + + +class Node: + def __init__(self, chr): + self.chr = chr + self.ended = False + self.children = defaultdict(lambda: None) + + +class Trie: + def __init__(self): + self.root = Node(None) # dummy + + @classmethod + def insert(cls, node, w, i): + if not node: + node = Node(w[i]) + + if i == len(w) - 1: + node.ended = True + else: + nxt = w[i + 1] + node.children[nxt] = cls.insert(node.children[nxt], w, i + 1) + + return node + + @classmethod + def search(cls, node, w, i): + if not node: + return + + if node.chr != w[i]: + return + + if node.ended: + return w[:i+1] + elif i + 1 < len(w): + return cls.search(node.children[w[i + 1]], w, i + 1) + else: + return + +class Solution: + def replaceWords(self, dic: List[str], sentence: str) -> str: + trie = Trie() + for word in dic: + root = trie.root + root.children[word[0]] = Trie.insert(root.children[word[0]], word, 0) + + ret = [] + for word in sentence.split(" "): + for child in trie.root.children.values(): + searched = Trie.search(child, word, 0) + if searched: + ret.append(searched) + break + else: + ret.append(word) + + return " ".join(ret) + + +if __name__ == "__main__": + assert Solution().replaceWords(["cat", "bat", "rat"], "the cattle was rattled by the battery") == "the cat was rat by the bat" From 367ae8eea438ced88b0e1dbf6b495efc099811dc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 20 Feb 2019 11:26:42 -0800 Subject: [PATCH 081/344] 650 --- 650 2 Keys Keyboard.py | 83 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 650 2 Keys Keyboard.py diff --git a/650 2 Keys Keyboard.py b/650 2 Keys Keyboard.py new file mode 100644 index 0000000..75c9692 --- /dev/null +++ b/650 2 Keys Keyboard.py @@ -0,0 +1,83 @@ +#!/usr/bin/python3 +""" +Initially on a notepad only one character 'A' is present. You can perform two +operations on this notepad for each step: + +Copy All: You can copy all the characters present on the notepad (partial copy +is not allowed). +Paste: You can paste the characters which are copied last time. + + +Given a number n. You have to get exactly n 'A' on the notepad by performing the +minimum number of steps permitted. Output the minimum number of steps to get n 'A'. + +Example 1: + +Input: 3 +Output: 3 +Explanation: +Intitally, we have one character 'A'. +In step 1, we use Copy All operation. +In step 2, we use Paste operation to get 'AA'. +In step 3, we use Paste operation to get 'AAA'. + + +Note: + +The n will be in the range [1, 1000]. +""" + + +class Solution: + def minSteps(self, n: int) -> int: + """ + Prime numger + To get 12 + We need to copy 6 (* 2) + To get 6 + We need to copy 2 (* 3) + To get 2 + We need to copy 1 (* 2) + """ + ret = 0 + for i in range(2, n+1): + while n % i == 0: + ret += i + n //= i + + return ret + + def minSteps_dp(self, n: int) -> int: + """ + Let F[i][j] be the minimum number to reach i A's with j copies + F[i][k] = min + F[i-k][k] + 1 + F[i/2][i/2] + 2 if i/2 == k + + Better dp: + F[i] = F[j] + j / i # copy j / i times + """ + F = [[float('inf') for _ in range(n+1)] for _ in range(n+1)] + F[1][0] = 0 + F[1][1] = 1 + for i in range(2, n + 1): + for j in range(i+1): + F[i][j] = min( + F[i][j], + F[i-j][j] + 1, + ) + if i % 2 == 0: + F[i][i//2] = min( + F[i][i//2], + F[i//2][j] + 2 + ) + + + ret = min(F[n]) + return ret + + +if __name__ == "__main__": + assert Solution().minSteps(7) == 7 + assert Solution().minSteps(3) == 3 + assert Solution().minSteps(4) == 4 From f1e6a742ad9354e80f7e92bf405e0e715beed220 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 20 Feb 2019 12:06:39 -0800 Subject: [PATCH 082/344] 653 --- 653 Two Sum IV - Input is a BST.py | 71 ++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 653 Two Sum IV - Input is a BST.py diff --git a/653 Two Sum IV - Input is a BST.py b/653 Two Sum IV - Input is a BST.py new file mode 100644 index 0000000..484156a --- /dev/null +++ b/653 Two Sum IV - Input is a BST.py @@ -0,0 +1,71 @@ +#!/usr/bin/python3 +""" +Given a Binary Search Tree and a target number, return true if there exist two +elements in the BST such that their sum is equal to the given target. + +Example 1: + +Input: + 5 + / \ + 3 6 + / \ \ +2 4 7 + +Target = 9 + +Output: True + + +Example 2: + +Input: + 5 + / \ + 3 6 + / \ \ +2 4 7 + +Target = 28 + +Output: False +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def findTarget(self, root: TreeNode, k: int) -> bool: + self.root = root + return self.walk(root, k) + + def walk(self, node, k): + if not node: + return False + + target = k - node.val + if self.find(self.root, target, node): + return True + + if self.walk(node.left, k) or self.walk(node.right, k): + return True + + return False + + def find(self, node, target, existing): + if not node: + return False + + if node.val == target: + return node != existing + + if target < node.val: + return self.find(node.left, target, existing) + else: + return self.find(node.right, target, existing) From f440a591a0421b253fc70c7655790fcb06bb9312 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 20 Feb 2019 15:23:12 -0800 Subject: [PATCH 083/344] 652 merkle tree --- 652 Find Duplicate Subtrees.py | 125 +++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 652 Find Duplicate Subtrees.py diff --git a/652 Find Duplicate Subtrees.py b/652 Find Duplicate Subtrees.py new file mode 100644 index 0000000..30c6fa7 --- /dev/null +++ b/652 Find Duplicate Subtrees.py @@ -0,0 +1,125 @@ +#!/usr/bin/python3 +""" +Given a binary tree, return all duplicate subtrees. For each kind of duplicate +subtrees, you only need to return the root node of any one of them. + +Two trees are duplicate if they have the same structure with same node values. + +Example 1: + + 1 + / \ + 2 3 + / / \ + 4 2 4 + / + 4 +The following are two duplicate subtrees: + + 2 + / + 4 +and + + 4 +Therefore, you need to return above trees' root in the form of a list. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +from typing import List +from collections import defaultdict + + +class MerkleHash: + def __init__(self): + self.start_key = 0 + self.merkle_hash = defaultdict(self._auto_incr) # subtree -> id + + def _auto_incr(self): + self.start_key += 1 + return self.start_key + + def __call__(self, val): + return self.merkle_hash[val] + +class Solution: + def __init__(self): + self.counter = defaultdict(int) + self.merkle_hash = MerkleHash() + + def findDuplicateSubtrees(self, root: TreeNode) -> List[TreeNode]: + """ + Merkle hash based on current val, and left substree merkle and right merkle + Assign each subtree a identity/hash + Chain of hash can uniquely identify a subtree + """ + ret = [] + self.walk(root, ret) + return ret + + def walk(self, cur, ret) -> int: + """ + return merkle hash id + """ + if not cur: + return self.merkle_hash(None) + + subtree_value = (cur.val, self.walk(cur.left, ret), self.walk(cur.right, ret)) + merkle_hash = self.merkle_hash(subtree_value) + if self.counter[merkle_hash] == 1: + ret.append(cur) + + self.counter[merkle_hash] += 1 + return merkle_hash + + +class Solution2: + def findDuplicateSubtrees(self, root: TreeNode) -> List[TreeNode]: + """ + Only need to return the root + """ + ret = [] + self.walk(root, defaultdict(int), ret) + return ret + + def walk(self, cur, counter, ret) -> str: + """ + serialize the subtrees and check existence + + Needs to have a unique representation + + for the key, cannot but cur.val in the middle as not be able to + differentiate between + + 0 + / + 0 + + 0 + \ + 0 + because you don't know which one is the root + + complexity: O(N) * O(N) (string concatenation), + """ + if not cur: + return "None" + + cur_key = ",".join([ + self.walk(cur.left, counter, ret), + self.walk(cur.right, counter, ret), + str(cur.val), + ]) + if counter[cur_key] == 1: + ret.append(cur) + + counter[cur_key] += 1 + return cur_key From df56b3f3f2dbfdb3d9e521b69c2d55b9b51030f1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 20 Feb 2019 16:14:35 -0800 Subject: [PATCH 084/344] 658 --- 652 Find Duplicate Subtrees.py | 1 + 658 Find K Closest Elements.py | 75 ++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 658 Find K Closest Elements.py diff --git a/652 Find Duplicate Subtrees.py b/652 Find Duplicate Subtrees.py index 30c6fa7..c2fd6d3 100644 --- a/652 Find Duplicate Subtrees.py +++ b/652 Find Duplicate Subtrees.py @@ -50,6 +50,7 @@ def _auto_incr(self): def __call__(self, val): return self.merkle_hash[val] + class Solution: def __init__(self): self.counter = defaultdict(int) diff --git a/658 Find K Closest Elements.py b/658 Find K Closest Elements.py new file mode 100644 index 0000000..8cc26fe --- /dev/null +++ b/658 Find K Closest Elements.py @@ -0,0 +1,75 @@ +#!/usr/bin/python3 +""" +Given a sorted array, two integers k and x, find the k closest elements to x in +the array. The result should also be sorted in ascending order. If there is a +tie, the smaller elements are always preferred. + +Example 1: +Input: [1,2,3,4,5], k=4, x=3 +Output: [1,2,3,4] +Example 2: +Input: [1,2,3,4,5], k=4, x=-1 +Output: [1,2,3,4] +Note: +The value k is positive and will always be smaller than the length of the sorted array. +Length of the given array is positive and will not exceed 104 +Absolute value of elements in the array and x will not exceed 104 +""" +from typing import List +from bisect import bisect_left +from collections import deque + + +class Solution: + def findClosestElements(self, A: List[int], k: int, x: int) -> List[int]: + """ + binary search without two pointers scanning + """ + n = len(A) + lo = 0 + hi = n - k + while lo < hi: + mid = (lo + hi) // 2 + if abs(x - A[mid]) > abs(A[mid + k] - x): + # better to have A[mid+k] rather than A[mid] + lo = mid + 1 + else: + hi = mid + + return A[lo:lo+k] + + def findClosestElements2(self, A: List[int], k: int, x: int) -> List[int]: + """ + input sorted arrya + two pointers + """ + n = len(A) + idx = bisect_left(A, x) + ret = deque() + i = idx - 1 + j = idx + while k: + if 0 <= i < n and 0 <= j < n: + if abs(A[i] - x) <= abs(A[j] - x): + ret.appendleft(A[i]) + i -= 1 + else: + ret.append(A[j]) + j += 1 + elif 0 <= i < n: + ret.appendleft(A[i]) + i -= 1 + elif 0 <= j < n: + ret.append(A[j]) + j += 1 + else: + raise + + k -= 1 + + return list(ret) + + +if __name__ == "__main__": + assert Solution().findClosestElements([1,2,3,4,5], 4, 3) == [1,2,3,4] + assert Solution().findClosestElements([1,2,3,4,5], 4, -1) == [1,2,3,4] From c517d0ceac6061f9a2a0a585db1e82d13e2449a8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 20 Feb 2019 22:05:16 -0800 Subject: [PATCH 085/344] 659 --- ...lit Array into Consecutive Subsequences.py | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 659 Split Array into Consecutive Subsequences.py diff --git a/659 Split Array into Consecutive Subsequences.py b/659 Split Array into Consecutive Subsequences.py new file mode 100644 index 0000000..8162c3c --- /dev/null +++ b/659 Split Array into Consecutive Subsequences.py @@ -0,0 +1,117 @@ +#!/usr/bin/python3 +""" +You are given an integer array sorted in ascending order (may contain +duplicates), you need to split them into several subsequences, where each +subsequences consist of at least 3 consecutive integers. Return whether you can +make such a split. + +Example 1: +Input: [1,2,3,3,4,5] +Output: True +Explanation: +You can split them into two consecutive subsequences : +1, 2, 3 +3, 4, 5 +Example 2: +Input: [1,2,3,3,4,4,5,5] +Output: True +Explanation: +You can split them into two consecutive subsequences : +1, 2, 3, 4, 5 +3, 4, 5 +Example 3: +Input: [1,2,3,4,4,5] +Output: False +Note: +The length of the input is in range of [1, 10000] +""" +from typing import List +from collections import defaultdict +import heapq + + +class Solution: + def isPossible(self, nums: List[int]) -> bool: + """ + Attribute a number to a existing consecutive subsequences + future numbers depend on this number to form the subsequence can also + attribtue to this existing subsequence + + If no existing one to attribtue, form a consecutive (l >= 3) by use the + subsequent numbers by looking forward + + Let F[i] be the number of consecutive subsequence at A[i] + """ + counter = defaultdict(int) + for e in nums: + counter[e] += 1 + + F = defaultdict(int) + for e in nums: + if counter[e] == 0: + continue + counter[e] -= 1 + + if F[e - 1] > 0: + F[e - 1] -= 1 + F[e] += 1 + elif counter[e + 1] > 0 and counter[e + 2] > 0: + F[e + 2] += 1 + counter[e + 1] -= 1 + counter[e + 2] -= 1 + else: + return False + + return True + + +class Interval: + def __init__(self, end, length): + self.end = end + self.length = length + + def __lt__(self, other): + if self.end == other.end: + return self.length < other.length + + return self.end < other.end + + def __repr__(self): + return repr((self.end, self.length)) + + +class Solution2: + def isPossible(self, nums: List[int]) -> bool: + """ + (length, last) + heap sortest first + >= 3, then drop + + split when duplicate + """ + h = [] + for n in nums: + while h and h[0].end + 1 < n: + itvl = heapq.heappop(h) + if itvl.length < 3: + return False + + if not h: + heapq.heappush(h, Interval(n, 1)) + elif h[0].end + 1 == n: + itvl = heapq.heappop(h) + heapq.heappush(h, Interval(n, itvl.length + 1)) + else: # n == end + heapq.heappush(h, Interval(n, 1)) + + + for itvl in h: + if itvl.length < 3: + return False + + return True + +if __name__ == "__main__": + assert Solution().isPossible([1,2,3,3,4,5]) == True + assert Solution().isPossible([1,2,3,3,4,4,5,5]) == True + assert Solution().isPossible([1,2,3,4,4,5]) == False From aedd1f02a023d411bb754faeb4888748bba17375 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 20 Feb 2019 22:05:23 -0800 Subject: [PATCH 086/344] 665 --- 665 Non-decreasing Array.py | 66 +++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 665 Non-decreasing Array.py diff --git a/665 Non-decreasing Array.py b/665 Non-decreasing Array.py new file mode 100644 index 0000000..803287c --- /dev/null +++ b/665 Non-decreasing Array.py @@ -0,0 +1,66 @@ +#!/usr/bin/python3 +""" +Given an array with n integers, your task is to check if it could become +non-decreasing by modifying at most 1 element. + +We define an array is non-decreasing if array[i] <= array[i + 1] holds for every +i (1 <= i < n). + +Example 1: +Input: [4,2,3] +Output: True +Explanation: You could modify the first 4 to 1 to get a non-decreasing array. +Example 2: +Input: [4,2,1] +Output: False +Explanation: You can't get a non-decreasing array by modify at most one element. +Note: The n belongs to [1, 10,000]. +""" +from typing import List + + +class Solution: + def checkPossibility(self, A: List[int]) -> bool: + """ + greedy change + two way of changing + """ + changed = False + for i in range(len(A) - 1): + if A[i] <= A[i + 1]: + continue + if not changed: + if i - 1 < 0 or A[i-1] <= A[i+1]: + A[i] = A[i+1] + else: + A[i+1] = A[i] + changed = True + else: + return False + + return True + + def checkPossibility_error(self, A: List[int]) -> bool: + """ + greedy change + """ + changed = False + for i in range(len(A) - 1): + if A[i] <= A[i + 1]: + continue + if not changed: + A[i] = A[i + 1] # Error + if i - 1 < 0 or A[i - 1] <= A[i]: + changed = True + else: + return False + else: + return False + + return True + + +if __name__ == "__main__": + assert Solution().checkPossibility([4,2,3]) == True + assert Solution().checkPossibility([3,4,2,3]) == False + assert Solution().checkPossibility([2,3,3,2,4]) == True From 14d2092148d8838feed88ffc0b6522c66df66ee1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 20 Feb 2019 22:25:59 -0800 Subject: [PATCH 087/344] 671 --- 671 Second Minimum Node In a Binary Tree.py | 67 +++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 671 Second Minimum Node In a Binary Tree.py diff --git a/671 Second Minimum Node In a Binary Tree.py b/671 Second Minimum Node In a Binary Tree.py new file mode 100644 index 0000000..961ed43 --- /dev/null +++ b/671 Second Minimum Node In a Binary Tree.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +Given a non-empty special binary tree consisting of nodes with the non-negative +value, where each node in this tree has exactly two or zero sub-node. If the +node has two sub-nodes, then this node's value is the smaller value among its +two sub-nodes. + +Given such a binary tree, you need to output the second minimum value in the set +made of all the nodes' value in the whole tree. + +If no such second minimum value exists, output -1 instead. + +Example 1: +Input: + 2 + / \ + 2 5 + / \ + 5 7 + +Output: 5 +Explanation: The smallest value is 2, the second smallest value is 5. +Example 2: +Input: + 2 + / \ + 2 2 + +Output: -1 +Explanation: The smallest value is 2, but there isn't any second smallest value. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def findSecondMinimumValue(self, root: TreeNode) -> int: + ret = self.find(root) + return -1 if ret == float('inf') else ret + + def find(self, root: TreeNode) -> int: + """ + find the second min + """ + if not root: + return float('inf') + + if root.left and root.right: + if root.left.val == root.val: + left = self.find(root.left) + else: + left = root.left.val + + if root.right.val == root.val: + right = self.find(root.right) + else: + right = root.right.val + + return min(left, right) + + return float('inf') From 424451f59901066892ec843149ca699f038c9452 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 22 Feb 2019 00:23:46 -0800 Subject: [PATCH 088/344] 686, 687 --- 686 Repeated String Match.py | 42 ++++++++++++++++ 687 Longest Univalue Path.py | 97 ++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 686 Repeated String Match.py create mode 100644 687 Longest Univalue Path.py diff --git a/686 Repeated String Match.py b/686 Repeated String Match.py new file mode 100644 index 0000000..e6deeed --- /dev/null +++ b/686 Repeated String Match.py @@ -0,0 +1,42 @@ +#!/usr/bin/python3 +""" +Given two strings A and B, find the minimum number of times A has to be repeated +such that B is a substring of it. If no such solution, return -1. + +For example, with A = "abcd" and B = "cdabcdab". + +Return 3, because by repeating A three times (“abcdabcdabcd”), B is a substring +of it; and B is not a substring of A repeated two times ("abcdabcd"). + +Note: +The length of A and B will be between 1 and 10000. +""" +import math + + +class Solution: + def repeatedStringMatch(self, A, B): + r = math.ceil(len(B) / len(A)) + for count in (r, r + 1): # r + 1 when len(B) % len(A) == 0 + if B in A * count: + return count + + return -1 + + def repeatedStringMatch_TLE(self, A: str, B: str) -> int: + for i in range(len(A)): + j = 0 + count = 0 + while j < len(B): + if i + j - count * len(A) >= len(A): + count += 1 + idx = i + j - count * len(A) + if A[idx] == B[j]: + j += 1 + else: + break + + if j == len(B): + return count + 1 + + return -1 diff --git a/687 Longest Univalue Path.py b/687 Longest Univalue Path.py new file mode 100644 index 0000000..6cc86a1 --- /dev/null +++ b/687 Longest Univalue Path.py @@ -0,0 +1,97 @@ +#!/usr/bin/python3 +""" +Given a binary tree, find the length of the longest path where each node in the +path has the same value. This path may or may not pass through the root. + +Note: The length of path between two nodes is represented by the number of edges +between them. + +Example 1: + +Input: + + 5 + / \ + 4 5 + / \ \ + 1 1 5 +Output: + +2 +Example 2: + +Input: + + 1 + / \ + 4 5 + / \ \ + 4 4 5 +Output: + +2 +Note: The given binary tree has not more than 10000 nodes. The height of the +tree is not more than 1000. +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.ret = 0 + + def longestUnivaluePath(self, root: TreeNode) -> int: + self.find(root) + return self.ret + + def find(self, node): + """ + the longest path ended at node + """ + if not node: + return 0 + + left = self.find(node.left) if node.left else 0 + right = self.find(node.right) if node.right else 0 + left_path = left + 1 if node.left and node.left.val == node.val else 0 + right_path = right + 1 if node.right and node.right.val == node.val else 0 + self.ret = max(self.ret, left_path + right_path) + return max(left_path, right_path) + + +class Solution_error: + def __init__(self): + self.ret = 0 + + def longestUnivaluePath(self, root: TreeNode) -> int: + self.find(root) + return self.ret + + def find(self, node): + """ + the longest path ended at node + """ + if not node: + return 0 + + left = self.find(node.left) + right = self.find(node.right) + cur = 1 # node.val + path = 1 + if left and node.left.val == node.val: + path += left + cur = left + 1 + + if right and node.right.val == node.val: + path += right + if right > left: + cur = right + 1 + + self.ret = max(self.ret, path - 1) + return cur From 4afd39bababc1d6795930451340935c27358aa81 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 24 Feb 2019 23:18:22 -0800 Subject: [PATCH 089/344] update --- 687 Longest Univalue Path.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/687 Longest Univalue Path.py b/687 Longest Univalue Path.py index 6cc86a1..53a14fe 100644 --- a/687 Longest Univalue Path.py +++ b/687 Longest Univalue Path.py @@ -57,8 +57,8 @@ def find(self, node): if not node: return 0 - left = self.find(node.left) if node.left else 0 - right = self.find(node.right) if node.right else 0 + left = self.find(node.left) + right = self.find(node.right) left_path = left + 1 if node.left and node.left.val == node.val else 0 right_path = right + 1 if node.right and node.right.val == node.val else 0 self.ret = max(self.ret, left_path + right_path) From 8bf40ecef110e27b74ce14210c3fd66e9888df8d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 24 Feb 2019 23:51:54 -0800 Subject: [PATCH 090/344] 688 --- 688 Knight Probability in Chessboard.py | 108 ++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 688 Knight Probability in Chessboard.py diff --git a/688 Knight Probability in Chessboard.py b/688 Knight Probability in Chessboard.py new file mode 100644 index 0000000..6389f72 --- /dev/null +++ b/688 Knight Probability in Chessboard.py @@ -0,0 +1,108 @@ +#!/usr/bin/python3 +""" +On an NxN chessboard, a knight starts at the r-th row and c-th column and +attempts to make exactly K moves. The rows and columns are 0 indexed, so the +top-left square is (0, 0), and the bottom-right square is (N-1, N-1). + +A chess knight has 8 possible moves it can make, as illustrated below. Each move +is two squares in a cardinal direction, then one square in an orthogonal +direction. + +[Image] + +Each time the knight is to move, it chooses one of eight possible moves +uniformly at random (even if the piece would go off the chessboard) and moves +there. + +The knight continues moving until it has made exactly K moves or has moved off +the chessboard. Return the probability that the knight remains on the board +after it has stopped moving. + +Example: + +Input: 3, 2, 0, 0 +Output: 0.0625 +Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on +the board. +From each of those positions, there are also two moves that will keep the knight +on the board. +The total probability the knight stays on the board is 0.0625. +""" +dirs = ( + (-1, -2), + (-1, 2), + (1, -2), + (1, 2), + (-2, -1), + (-2, 1), + (2, -1), + (2, 1), +) + + +class Solution: + def knightProbability(self, N: int, K: int, r: int, c: int) -> float: + """ + brute force K step + + with memory, it is considered dp + """ + q = set([(r, c)]) # working que + P = [[0 for _ in range(N)] for _ in range(N)] + P[r][c] = 1 # optimize memory + k = 0 + while k < K: + k += 1 + cur_q = set() + cur_P = [[0 for _ in range(N)] for _ in range(N)] + for i, j in q: + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < N and 0 <= J < N: + cur_q.add((I, J)) + cur_P[I][J] += P[i][j] * 1 / 8 + + q = cur_q + P = cur_P + + return sum([ + P[i][j] + for i in range(N) + for j in range(N) + ]) + + + def knightProbability_error(self, N: int, K: int, r: int, c: int) -> float: + """ + brute force K step + """ + q = [(r, c)] # working que + P = [[0 for _ in range(N)] for _ in range(N)] + P[r][c] = 1 # optimize memory + k = 0 + while k < K: + k += 1 + cur_q = [] + cur_P = [[0 for _ in range(N)] for _ in range(N)] + for i, j in q: + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < N and 0 <= J < N: + cur_q.append((I, J)) # error, count multiple times + cur_P[I][J] += P[i][j] * 1 / 8 + + q = cur_q + P = cur_P + + return sum([ + P[i][j] + for i in range(N) + for j in range(N) + ]) + + +if __name__ == "__main__": + assert Solution().knightProbability(3, 2, 0, 0) == 0.0625 + assert Solution().knightProbability(3, 3, 0, 0) == 0.015625 From 69af9c19a9ebb5b038fd28bfd9f35d05138073b9 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 25 Feb 2019 00:11:19 -0800 Subject: [PATCH 091/344] 692 --- 692 Top K Frequent Words.py | 67 +++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 692 Top K Frequent Words.py diff --git a/692 Top K Frequent Words.py b/692 Top K Frequent Words.py new file mode 100644 index 0000000..c66f0ce --- /dev/null +++ b/692 Top K Frequent Words.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +Given a non-empty list of words, return the k most frequent elements. + +Your answer should be sorted by frequency from highest to lowest. If two words +have the same frequency, then the word with the lower alphabetical order comes +first. + +Example 1: +Input: ["i", "love", "leetcode", "i", "love", "coding"], k = 2 +Output: ["i", "love"] +Explanation: "i" and "love" are the two most frequent words. + Note that "i" comes before "love" due to a lower alphabetical order. +Example 2: +Input: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4 +Output: ["the", "is", "sunny", "day"] +Explanation: "the", "is", "sunny" and "day" are the four most frequent words, + with the number of occurrence being 4, 3, 2 and 1 respectively. +Note: +You may assume k is always valid, 1 ≤ k ≤ number of unique elements. +Input words contain only lowercase letters. +Follow up: +Try to solve it in O(n log k) time and O(n) extra space. +""" +import heapq +from collections import defaultdict +from typing import List + + +class Word: + def __init__(self, content, count): + self.content = content + self.count = count + + def __lt__(self, other): + if self.count == other.count: + return self.content > other.content + + return self.count < other.count + + +class Solution: + def topKFrequent(self, words: List[str], k: int) -> List[str]: + """ + quick select log n + heap log k + """ + h = [] + counter = defaultdict(int) + for w in words: + counter[w] += 1 + + for w, c in counter.items(): + heapq.heappush(h, Word(w, c)) + if len(h) > k: + heapq.heappop(h) + + ret = [] + while h: + w = heapq.heappop(h).content + ret.append(w) + + return ret[::-1] + + +if __name__ == "__main__": + assert Solution().topKFrequent(["i", "love", "leetcode", "i", "love", "coding"], 2) From d591db32cff2721f4e1cf2f47dc0eada61a4b78d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Feb 2019 14:27:38 -0800 Subject: [PATCH 092/344] 695 --- 695 Max Area of Island.py | 74 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 695 Max Area of Island.py diff --git a/695 Max Area of Island.py b/695 Max Area of Island.py new file mode 100644 index 0000000..4fc796f --- /dev/null +++ b/695 Max Area of Island.py @@ -0,0 +1,74 @@ +#!/usr/bin/python3 +""" +Given a non-empty 2D array grid of 0's and 1's, an island is a group of 1's +(representing land) connected 4-directionally (horizontal or vertical.) You may +assume all four edges of the grid are surrounded by water. + +Find the maximum area of an island in the given 2D array. (If there is no +island, the maximum area is 0.) + +Example 1: + +[[0,0,1,0,0,0,0,1,0,0,0,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,1,1,0,1,0,0,0,0,0,0,0,0], + [0,1,0,0,1,1,0,0,1,0,1,0,0], + [0,1,0,0,1,1,0,0,1,1,1,0,0], + [0,0,0,0,0,0,0,0,0,0,1,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,0,0,0,0,0,0,1,1,0,0,0,0]] +Given the above grid, return 6. Note the answer is not 11, because the island +must be connected 4-directionally. +Example 2: + +[[0,0,0,0,0,0,0,0]] +Given the above grid, return 0. +Note: The length of each dimension in the given grid does not exceed 50. +""" +from typing import List + + +dirs = ((0, -1), (0, 1), (-1, 0), (1, 0)) + + +class Solution: + def maxAreaOfIsland(self, grid: List[List[int]]) -> int: + """ + dfs + """ + if not grid: + return 0 + + ret = 0 + m, n = len(grid), len(grid[0]) + visited = [[False for _ in range(n)] for _ in range(m)] + for i in range(m): + for j in range(n): + if not visited[i][j] and grid[i][j] == 1: + ret = max(ret, self.dfs(grid, i, j, visited)) + + return ret + + def dfs(self, grid, i, j, visited) -> int: + visited[i][j] = True + ret = 1 + m, n = len(grid), len(grid[0]) + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < m and 0 <= J < n and not visited[I][J] and grid[I][J] == 1: + ret += self.dfs(grid, I, J, visited) + + return ret + + +if __name__ == "__main__": + grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,1,1,0,1,0,0,0,0,0,0,0,0], + [0,1,0,0,1,1,0,0,1,0,1,0,0], + [0,1,0,0,1,1,0,0,1,1,1,0,0], + [0,0,0,0,0,0,0,0,0,0,1,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,0,0,0,0,0,0,1,1,0,0,0,0]] + assert Solution().maxAreaOfIsland(grid) == 6 From 56bcd5560c26416b4fad20a0b9f2c550ac67f9ee Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Feb 2019 14:33:15 -0800 Subject: [PATCH 093/344] 693 --- 693 Binary Number with Alternating Bits.py | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 693 Binary Number with Alternating Bits.py diff --git a/693 Binary Number with Alternating Bits.py b/693 Binary Number with Alternating Bits.py new file mode 100644 index 0000000..87f4ddc --- /dev/null +++ b/693 Binary Number with Alternating Bits.py @@ -0,0 +1,34 @@ +#!/usr/bin/python3 +""" +Given a positive integer, check whether it has alternating bits: namely, if two +adjacent bits will always have different values. + +Example 1: +Input: 5 +Output: True +Explanation: +The binary representation of 5 is: 101 +Example 2: +Input: 7 +Output: False +Explanation: +The binary representation of 7 is: 111. +""" + + +class Solution: + def hasAlternatingBits(self, n: int) -> bool: + last = None + while n: + cur = n & 1 + if last is not None and last ^ cur == 0: + return False + last = cur + n >>= 1 + + return True + + +if __name__ == "__main__": + assert Solution().hasAlternatingBits(5) == True + assert Solution().hasAlternatingBits(7) == False From 9aecc1e68bca90a96d168e43faf0f8e75b481a82 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Feb 2019 15:15:54 -0800 Subject: [PATCH 094/344] 696 --- 693 Binary Number with Alternating Bits.py | 1 + 696 Count Binary Substrings.py | 69 ++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 696 Count Binary Substrings.py diff --git a/693 Binary Number with Alternating Bits.py b/693 Binary Number with Alternating Bits.py index 87f4ddc..0163daf 100644 --- a/693 Binary Number with Alternating Bits.py +++ b/693 Binary Number with Alternating Bits.py @@ -21,6 +21,7 @@ def hasAlternatingBits(self, n: int) -> bool: last = None while n: cur = n & 1 + # `if last` is error if last is not None and last ^ cur == 0: return False last = cur diff --git a/696 Count Binary Substrings.py b/696 Count Binary Substrings.py new file mode 100644 index 0000000..5f90b4a --- /dev/null +++ b/696 Count Binary Substrings.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +""" +Give a string s, count the number of non-empty (contiguous) substrings that have +the same number of 0's and 1's, and all the 0's and all the 1's in these +substrings are grouped consecutively. + +Substrings that occur multiple times are counted the number of times they occur. + +Example 1: +Input: "00110011" +Output: 6 +Explanation: There are 6 substrings that have equal number of consecutive 1's +and 0's: "0011", "01", "1100", "10", "0011", and "01". + +Notice that some of these substrings repeat and are counted the number of times +they occur. + +Also, "00110011" is not a valid substring because all the 0's (and 1's) are not +grouped together. +Example 2: +Input: "10101" +Output: 4 +Explanation: There are 4 substrings: "10", "01", "10", "01" that have equal +number of consecutive 1's and 0's. +""" + + +class Solution: + def countBinarySubstrings(self, s: str) -> int: + """ + two-pointers + math + """ + cur = 1 # 0 1 symmetry, no need 0, 1 counter, only need cur and prev counter + prev = 0 + ret = 0 + for i in range(1, len(s)): + if s[i] == s[i-1]: + cur += 1 + else: + prev = cur + cur = 1 + if prev >= cur: + ret += 1 + + return ret + + def countBinarySubstrings_error(self, s: str) -> int: + """ + two-pointers + math + """ + counter = {"0": 0, "1": 0} + ret = 0 + if not s: + return ret + counter[s[0]] += 1 + for i in range(1, len(s)): + if s[i] != s[i-1] and counter[s[i]] != 0: + counter[s[i]] = 0 + + counter[s[i]] += 1 + if min(counter["0"], counter["1"]) > 0: + ret += 1 + + return ret + + +if __name__ == "__main__": + assert Solution().countBinarySubstrings("00110011") == 6 + assert Solution().countBinarySubstrings("00110") == 3 From 8ea2627e055cc27a2b85f0d190f995e0937574a7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Feb 2019 15:41:15 -0800 Subject: [PATCH 095/344] 700 --- 700 Search in a Binary Search Tree.py | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 700 Search in a Binary Search Tree.py diff --git a/700 Search in a Binary Search Tree.py b/700 Search in a Binary Search Tree.py new file mode 100644 index 0000000..a99a6a6 --- /dev/null +++ b/700 Search in a Binary Search Tree.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" +Given the root node of a binary search tree (BST) and a value. You need to +find the node in the BST that the node's value equals the given value. Return +the subtree rooted with that node. If such node doesn't exist, you should return +NULL. + +For example, + +Given the tree: + 4 + / \ + 2 7 + / \ + 1 3 + +And the value to search: 2 +You should return this subtree: + + 2 + / \ + 1 3 +In the example above, if we want to search the value 5, since there is no node +with value 5, we should return NULL. + +Note that an empty tree is represented by NULL, therefore you would see the +expected output (serialized tree format) as [], not null. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def searchBST(self, root: TreeNode, val: int) -> TreeNode: + if not root: + return None + + if root.val == val: + return root + elif root.val < val: + return self.searchBST(root.right, val) + else: + return self.searchBST(root.left, val) From 1d22cc1a3875e1d587360c2ac2aa6037c4d148ed Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Feb 2019 16:50:48 -0800 Subject: [PATCH 096/344] 698 --- 698 Partition to K Equal Sum Subsets.py | 107 ++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 698 Partition to K Equal Sum Subsets.py diff --git a/698 Partition to K Equal Sum Subsets.py b/698 Partition to K Equal Sum Subsets.py new file mode 100644 index 0000000..318e128 --- /dev/null +++ b/698 Partition to K Equal Sum Subsets.py @@ -0,0 +1,107 @@ +#!/usr/bin/python3 +""" +Given an array of integers nums and a positive integer k, find whether it's +possible to divide this array into k non-empty subsets whose sums are all equal. + +Example 1: + +Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4 +Output: True +Explanation: It's possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) +with equal sums. + + +Note: + +1 <= k <= len(nums) <= 16. +0 < nums[i] < 10000. +""" +from typing import List + + +class Solution: + def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: + """ + resurive search + """ + s = sum(nums) + if s % k != 0: + return False + + target = s // k + visited = [False for _ in nums] + return self.dfs(nums, 0, None, target, visited, k) + + def dfs(self, nums, start_idx, cur_sum, target_sum, visited, k): + """ + some corner cases: + 1. target_sum default at 0: sum or empty array is 0? + 2. nxt_sum = (cur_sum or 0) + nums[i] rather than cur_sum or 0 + nums[i] + arithmetic operator has higher precedence than logic operator + + start index to prune + """ + if k == 1: + return True + + if cur_sum and cur_sum == target_sum: + # start index is 0 + return self.dfs(nums, 0, None, target_sum, visited, k - 1) + + for i in range(start_idx, len(nums)): + if not visited[i]: + # corner case target_sum is 0 + visited[i] = True + nxt_sum = (cur_sum or 0) + nums[i] + # error when cur_sum or 0 + nums[i] + # arithmetic operator has higher precedence than logic operator + if self.dfs(nums, i + 1, nxt_sum, target_sum, visited, k): + return True + visited[i] = False + + return False + + +class Solution_TLE: + def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: + """ + resurive search + """ + s = sum(nums) + if s % k != 0: + return False + + target = s // k + visited = [False for _ in nums] + return self.dfs(nums, None, target, visited, k) + + def dfs(self, nums, cur_sum, target_sum, visited, k): + """ + some corner cases: + 1. target_sum default at 0: sum or empty array is 0? + 2. nxt_sum = (cur_sum or 0) + nums[i] rather than cur_sum or 0 + nums[i] + arithmetic operator has higher precedence than logic operator + """ + if k == 0: + return True + + if cur_sum and cur_sum == target_sum: + return self.dfs(nums, None, target_sum, visited, k - 1) + + for i in range(len(nums)): + if not visited[i]: + # corner case target_sum is 0 + visited[i] = True + nxt_sum = (cur_sum or 0) + nums[i] + # error when cur_sum or 0 + nums[i] + # arithmetic operator has higher precedence than logic operator + if self.dfs(nums, nxt_sum, target_sum, visited, k): + return True + visited[i] = False + + return False + + +if __name__ == "__main__": + assert Solution().canPartitionKSubsets([5, 3, 2, 3, 1, 2, 4], 4) == True + assert Solution().canPartitionKSubsets([4, 3, 2, 3, 5, 2, 1], 4) == True From d92c302cb51d4c1d786d766bddbe86a784794570 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Feb 2019 17:29:40 -0800 Subject: [PATCH 097/344] 697 --- 697 Degree of an Array.py | 54 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 697 Degree of an Array.py diff --git a/697 Degree of an Array.py b/697 Degree of an Array.py new file mode 100644 index 0000000..fef11be --- /dev/null +++ b/697 Degree of an Array.py @@ -0,0 +1,54 @@ +#!/usr/bin/python3 +""" +Given a non-empty array of non-negative integers nums, the degree of this array +is defined as the maximum frequency of any one of its elements. + +Your task is to find the smallest possible length of a (contiguous) subarray of +nums, that has the same degree as nums. + +Example 1: +Input: [1, 2, 2, 3, 1] +Output: 2 +Explanation: +The input array has a degree of 2 because both elements 1 and 2 appear twice. +Of the subarrays that have the same degree: +[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2] +The shortest length is 2. So return 2. +Example 2: +Input: [1,2,2,3,1,4,2] +Output: 6 +Note: + +nums.length will be between 1 and 50,000. +nums[i] will be an integer between 0 and 49,999. +""" +from typing import List +from collections import defaultdict + + +class Solution: + def findShortestSubArray(self, nums: List[int]) -> int: + """ + counter + two pointers does not work + counter + first appearance + """ + if not nums: + return + + counter = defaultdict(int) + first = {} + mx = [0, 0] # [degree, length] + for i, n in enumerate(nums): + if n not in first: + first[n] = i # setdefault + counter[n] += 1 + if counter[n] > mx[0]: + mx = [counter[n], i - first[n] + 1] + elif counter[n] == mx[0]: + mx[1] = min(mx[1], i - first[n] + 1) + + return mx[1] + + +if __name__ == "__main__": + assert Solution().findShortestSubArray([1, 2, 2, 3, 1]) == 2 From 61107f67efc0f57a3254a2bfff516bd90faef58c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Feb 2019 22:01:01 -0800 Subject: [PATCH 098/344] 646 --- 646 Maximum Length of Pair Chain.py | 85 +++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 646 Maximum Length of Pair Chain.py diff --git a/646 Maximum Length of Pair Chain.py b/646 Maximum Length of Pair Chain.py new file mode 100644 index 0000000..23e01ce --- /dev/null +++ b/646 Maximum Length of Pair Chain.py @@ -0,0 +1,85 @@ +#!/usr/bin/python3 +""" +You are given n pairs of numbers. In every pair, the first number is always +smaller than the second number. + +Now, we define a pair (c, d) can follow another pair (a, b) if and only if +b < c. Chain of pairs can be formed in this fashion. + +Given a set of pairs, find the length longest chain which can be formed. You +needn't use up all the given pairs. You can select pairs in any order. + +Example 1: +Input: [[1,2], [2,3], [3,4]] +Output: 2 +Explanation: The longest chain is [1,2] -> [3,4] +Note: +The number of given pairs will be in the range [1, 1000]. +""" +from typing import List + + +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + """ + Greedy + sort by the interval end + similar to 435 Non-overlaping interval + O(nlg n) + O(n) + """ + pairs.sort(key=lambda x: x[1]) + n = len(pairs) + + ret = 0 + cur_end = -float("inf") + for i in range(n): + if pairs[i][0] <= cur_end: + continue + + cur_end = pairs[i][1] + ret += 1 + + return ret + + def findLongestChain2(self, pairs: List[List[int]]) -> int: + """ + Greedy + sort by the interval end + similar to 435 Non-overlaping interval + """ + pairs.sort(key=lambda x: x[1]) + n = len(pairs) + + ret = 0 + i = 0 + while i < n: + ret += 1 + cur_end = pairs[i][1] + + i += 1 + while i < n and pairs[i][0] <= cur_end: + i += 1 + + return ret + + +class Solution2: + def findLongestChain(self, pairs: List[List[int]]) -> int: + """ + Let F[i] be the longest chain ended at A[i] + F[i] = max(F[j] + 1 if predicate A[i] A[j]) + O(N^2) + """ + pairs.sort(key=lambda x: tuple(x)) + n = len(pairs) + F = [1 for _ in range(n)] + for i in range(n): + for j in range(i): + if pairs[j][1] < pairs[i][0]: + F[i] = max(F[i], F[j] + 1) + + return max(F) + + +if __name__ == "__main__": + assert Solution().findLongestChain([[1,2], [2,3], [3,4]]) == 2 From efc6eea02dc3ad236213591a12fb6630891a2bf4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Feb 2019 22:13:24 -0800 Subject: [PATCH 099/344] 701 --- 701 Insert into a Binary Search Tree.py | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 701 Insert into a Binary Search Tree.py diff --git a/701 Insert into a Binary Search Tree.py b/701 Insert into a Binary Search Tree.py new file mode 100644 index 0000000..ca541bd --- /dev/null +++ b/701 Insert into a Binary Search Tree.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +""" +Given the root node of a binary search tree (BST) and a value to be inserted +into the tree, insert the value into the BST. Return the root node of the BST +after the insertion. It is guaranteed that the new value does not exist in the +original BST. + +Note that there may exist multiple valid ways for the insertion, as long as the +tree remains a BST after insertion. You can return any of them. + +For example, + +Given the tree: + 4 + / \ + 2 7 + / \ + 1 3 +And the value to insert: 5 +You can return this binary search tree: + + 4 + / \ + 2 7 + / \ / + 1 3 5 +This tree is also valid: + + 5 + / \ + 2 7 + / \ + 1 3 + \ + 4 +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode: + if not root: + return TreeNode(val) + + if root.val < val: + root.right = self.insertIntoBST(root.right, val) + elif root.val > val: + root.left = self.insertIntoBST(root.left, val) + else: + raise + + return root From fa6a5475c30c73994735502d0b4e3f94d7d821eb Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Feb 2019 22:18:21 -0800 Subject: [PATCH 100/344] 637 --- 637 Average of Levels in Binary Tree.py | 53 +++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 637 Average of Levels in Binary Tree.py diff --git a/637 Average of Levels in Binary Tree.py b/637 Average of Levels in Binary Tree.py new file mode 100644 index 0000000..ea43ab0 --- /dev/null +++ b/637 Average of Levels in Binary Tree.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +""" +Given a non-empty binary tree, return the average value of the nodes on each +level in the form of an array. +Example 1: +Input: + 3 + / \ + 9 20 + / \ + 15 7 +Output: [3, 14.5, 11] +Explanation: +The average value of nodes on level 0 is 3, on level 1 is 14.5, and on level 2 +is 11. Hence return [3, 14.5, 11]. +Note: +The range of node's value is in the range of 32-bit signed integer. +""" +from typing import List + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def averageOfLevels(self, root: TreeNode) -> List[float]: + """ + BFS + """ + ret = [] + if not root: + return ret + + q = [root] + while q: + n = len(q) + avg = sum(map(lambda node: node.val, q)) / n + ret.append(avg) + cur_q = [] + for node in q: + if node.left: + cur_q.append(node.left) + if node.right: + cur_q.append(node.right) + + q = cur_q + + return ret From 9aec39313a1447424ac90c209a453c3aba26a272 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Feb 2019 22:34:46 -0800 Subject: [PATCH 101/344] 628 --- 628 Maximum Product of Three Numbers.py | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 628 Maximum Product of Three Numbers.py diff --git a/628 Maximum Product of Three Numbers.py b/628 Maximum Product of Three Numbers.py new file mode 100644 index 0000000..02caaf4 --- /dev/null +++ b/628 Maximum Product of Three Numbers.py @@ -0,0 +1,40 @@ +#!/usr/bin/python3 +""" +Given an integer array, find three numbers whose product is maximum and output +the maximum product. + +Example 1: + +Input: [1,2,3] +Output: 6 + + +Example 2: + +Input: [1,2,3,4] +Output: 24 + + +Note: + +The length of the given array will be in range [3,104] and all elements are in +the range [-1000, 1000]. +Multiplication of any three numbers in the input won't exceed the range of +32-bit signed integer. +""" +import heapq + +from typing import List + + +class Solution: + def maximumProduct(self, nums: List[int]) -> int: + """ + heapq nlargest nsmallest + """ + mxes = heapq.nlargest(3, nums) + mns = heapq.nsmallest(3, nums) + return max( + mxes[0] * mxes[1] * mxes[2], + mns[0] * mns[1] * mxes[0], + ) From 23f4afc049c49d47b1ce9edc6e5bf16138a9fbba Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Feb 2019 23:08:55 -0800 Subject: [PATCH 102/344] 645 --- 645 Set Mismatch.py | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 645 Set Mismatch.py diff --git a/645 Set Mismatch.py b/645 Set Mismatch.py new file mode 100644 index 0000000..300bfa8 --- /dev/null +++ b/645 Set Mismatch.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 +""" +The set S originally contains numbers from 1 to n. But unfortunately, due to the +data error, one of the numbers in the set got duplicated to another number in +the set, which results in repetition of one number and loss of another number. + +Given an array nums representing the data status of this set after the error. +Your task is to firstly find the number occurs twice and then find the number +that is missing. Return them in the form of an array. + +Example 1: +Input: nums = [1,2,2,4] +Output: [2,3] +Note: +The given array size will in the range [2, 10000]. +The given array's numbers won't have any order. +""" +from typing import List + + +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + """ + https://leetcode.com/problems/set-mismatch/discuss/113999/C%2B%2B-True-O(1)-space-O(n)-time-(No-input-modifying)-with-clear-explanation + """ + n = len(nums) + acc0 = 0 # a ^ b + for i in range(n): + acc0 ^= nums[i] + acc0 ^= i + 1 + + first_1 = acc0 & - acc0 # 2's complement, invert the bit left to the first 1 from the right + # go through the arrays once again and split them in 2 categories, if they have that bit set or not + # xor them to get a or b + acc1 = 0 + acc2 = 0 + for i in range(n): + if nums[i] & first_1: + acc1 ^= nums[i] + else: + acc2 ^= nums[i] + + if (i + 1) & first_1: + acc1 ^= i + 1 + else: + acc2 ^= i + 1 + + for i in range(n): + if nums[i] == acc1: + return [acc1, acc2] + + return [acc2, acc1] From 8383038fa522d763d04414affef9665dc00b533a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 27 Feb 2019 09:00:48 -0800 Subject: [PATCH 103/344] 703 --- 703 Kth Largest Element in a Stream.py | 53 ++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 703 Kth Largest Element in a Stream.py diff --git a/703 Kth Largest Element in a Stream.py b/703 Kth Largest Element in a Stream.py new file mode 100644 index 0000000..4d34d3f --- /dev/null +++ b/703 Kth Largest Element in a Stream.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +""" +Design a class to find the kth largest element in a stream. Note that it is the +kth largest element in the sorted order, not the kth distinct element. + +Your KthLargest class will have a constructor which accepts an integer k and an +integer array nums, which contains initial elements from the stream. For each +call to the method KthLargest.add, return the element representing the kth +largest element in the stream. + +Example: + +int k = 3; +int[] arr = [4,5,8,2]; +KthLargest kthLargest = new KthLargest(3, arr); +kthLargest.add(3); // returns 4 +kthLargest.add(5); // returns 5 +kthLargest.add(10); // returns 5 +kthLargest.add(9); // returns 8 +kthLargest.add(4); // returns 8 +Note: +You may assume that nums' length ≥ k-1 and k ≥ 1. +""" +from typing import List +import heapq + + +class KthLargest: + + def __init__(self, k: int, nums: List[int]): + """ + heap + min-heap, since we want the head be the k-th largest + """ + self.h = [] + self.k = k + for n in nums: + self.add(n) + + def add(self, val: int) -> int: + heapq.heappush(self.h, val) + if len(self.h) > self.k: + heapq.heappop(self.h) + + return self.h[0] + + + + + +# Your KthLargest object will be instantiated and called as such: +# obj = KthLargest(k, nums) +# param_1 = obj.add(val) From a119b3c3b4fdcd29001bb6b2c17faf62a13a5107 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 28 Feb 2019 22:29:49 -0800 Subject: [PATCH 104/344] 718 --- 718 Maximum Length of Repeated Subarray.py | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 718 Maximum Length of Repeated Subarray.py diff --git a/718 Maximum Length of Repeated Subarray.py b/718 Maximum Length of Repeated Subarray.py new file mode 100644 index 0000000..74f03f3 --- /dev/null +++ b/718 Maximum Length of Repeated Subarray.py @@ -0,0 +1,47 @@ +#!/usr/bin/python3 +""" +Given two integer arrays A and B, return the maximum length of an subarray that +appears in both arrays. + +Example 1: +Input: +A: [1,2,3,2,1] +B: [3,2,1,4,7] +Output: 3 +Explanation: +The repeated subarray with maximum length is [3, 2, 1]. +Note: +1 <= len(A), len(B) <= 1000 +0 <= A[i], B[i] < 100 +""" +from typing import List +from collections import defaultdict + + +class Solution: + def findLength(self, A: List[int], B: List[int]) -> int: + """ + similar to longest substring + Brute force O(mn) + + DP - O(mn) + possible + F[i][j] be the longest substring ended at A[i-1], B[i-1] + F[i][j] = F[i-1][j-1] + 1 if A[i-1] == B[i-1] else 0 + """ + m, n = len(A), len(B) + F = defaultdict(lambda: defaultdict(int)) + for i in range(1, m+1): + for j in range(1, n+1): + if A[i-1] == B[j-1]: + F[i][j] = F[i-1][j-1] + 1 + + return max( + F[i][j] + for i in range(1, m+1) + for j in range(1, n+1) + ) + + +if __name__ == "__main__": + assert Solution().findLength([1,2,3,2,1], [3,2,1,4,7]) == 3 From 4988a0ae7dd1741c1a44463564d45dfd7d632c18 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 28 Feb 2019 23:40:17 -0800 Subject: [PATCH 105/344] 712 --- ...inimum ASCII Delete Sum for Two Strings.py | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 712 Minimum ASCII Delete Sum for Two Strings.py diff --git a/712 Minimum ASCII Delete Sum for Two Strings.py b/712 Minimum ASCII Delete Sum for Two Strings.py new file mode 100644 index 0000000..6cbdcbe --- /dev/null +++ b/712 Minimum ASCII Delete Sum for Two Strings.py @@ -0,0 +1,93 @@ +#!/usr/bin/python3 +""" +Given two strings s1, s2, find the lowest ASCII sum of deleted characters to make +two strings equal. + +Example 1: +Input: s1 = "sea", s2 = "eat" +Output: 231 +Explanation: Deleting "s" from "sea" adds the ASCII value of "s" (115) to the +sum. +Deleting "t" from "eat" adds 116 to the sum. +At the end, both strings are equal, and 115 + 116 = 231 is the minimum sum +possible to achieve this. + +Example 2: +Input: s1 = "delete", s2 = "leet" +Output: 403 +Explanation: Deleting "dee" from "delete" to turn the string into "let", +adds 100[d]+101[e]+101[e] to the sum. Deleting "e" from "leet" adds 101[e] to the sum. +At the end, both strings are equal to "let", and the answer is 100+101+101+101 = 403. +If instead we turned both strings into "lee" or "eet", we would get answers of 433 or 417, which are higher. +Note: + +0 < s1.length, s2.length <= 1000. +All elements of each string will have an ASCII value in [97, 122]. +""" + + +class Solution: + def minimumDeleteSum(self, s1: str, s2: str) -> int: + """ + let F[i][j] be the cost to delete & make s1[:i] == s2[:j] + F[i][j] = min + F[i][j-1] + cost (delete s2[j-1], and then delete & make s1[:i] == s2[:j-1]) + F[i-1][j] + cost + F[i-1][j-1] if (s1[i-1] == s2[j-1]) + """ + m, n = len(s1), len(s2) + F = [[float('inf') for _ in range(n + 1)] for _ in range(m + 1)] + F[0][0] = 0 + for i in range(1, m + 1): + F[i][0] = F[i-1][0] + ord(s1[i-1]) + for j in range(1, n + 1): + F[0][j] = F[0][j-1] + ord(s2[j-1]) + for i in range(1, m + 1): + for j in range(1, n + 1): + F[i][j] = min( + F[i][j], + F[i][j-1] + ord(s2[j-1]), + F[i-1][j] + ord(s1[i-1]), + ) + if s1[i-1] == s2[j-1]: + F[i][j] = min( + F[i][j], + F[i-1][j-1], + ) + + return F[m][n] + + def minimumDeleteSum_error(self, s1: str, s2: str) -> int: + """ + let F[i][j] be the cost to make s1[:i] == s2[:j] + F[i][j] = min + F[i][j-1] + cost (delete s2[j-1]) + F[i-1][j] + cost + F[i-1][j-1] if (s1[i-1] == s2[j-1]) + + Error at initial conditions + """ + m, n = len(s1), len(s2) + F = [[float('inf') for _ in range(n + 1)] for _ in range(m + 1)] + F[0][0] = 0 + F[1][0] = ord(s1[0]) + F[0][1] = ord(s2[0]) + for i in range(1, m + 1): + for j in range(1, n + 1): + F[i][j] = min( + F[i][j], + F[i][j-1] + ord(s2[j-1]), + F[i-1][j] + ord(s1[i-1]), + ) + if s1[i-1] == s2[j-1]: + F[i][j] = min( + F[i][j], + F[i-1][j-1], + ) + + return F[m][n] + + +if __name__ == "__main__": + assert Solution().minimumDeleteSum("sea", "eat") == 231 + assert Solution().minimumDeleteSum("delete", "leet") == 403 From 0f20bec0fa2958c7f5724a00b05a4fabfc29443b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 1 Mar 2019 23:57:16 -0800 Subject: [PATCH 106/344] 733 --- 733 Flood Fill.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 733 Flood Fill.py diff --git a/733 Flood Fill.py b/733 Flood Fill.py new file mode 100644 index 0000000..4350e4e --- /dev/null +++ b/733 Flood Fill.py @@ -0,0 +1,63 @@ +#!/usr/bin/python3 +""" +An image is represented by a 2-D array of integers, each integer representing +the pixel value of the image (from 0 to 65535). + +Given a coordinate (sr, sc) representing the starting pixel (row and column) of +the flood fill, and a pixel value newColor, "flood fill" the image. + +To perform a "flood fill", consider the starting pixel, plus any pixels +connected 4-directionally to the starting pixel of the same color as the +starting pixel, plus any pixels connected 4-directionally to those pixels (also +with the same color as the starting pixel), and so on. Replace the color of all +of the aforementioned pixels with the newColor. + +At the end, return the modified image. + +Example 1: +Input: +image = [[1,1,1],[1,1,0],[1,0,1]] +sr = 1, sc = 1, newColor = 2 +Output: [[2,2,2],[2,2,0],[2,0,1]] +Explanation: +From the center of the image (with position (sr, sc) = (1, 1)), all pixels +connected +by a path of the same color as the starting pixel are colored with the new +color. +Note the bottom corner is not colored 2, because it is not 4-directionally +connected +to the starting pixel. +Note: + +The length of image and image[0] will be in the range [1, 50]. +The given starting pixel will satisfy 0 <= sr < image.length and 0 <= sc < +image[0].length. +The value of each color in image[i][j] and newColor will be an integer in +[0, 65535]. +""" +from typing import List +dirs = ((-1, 0), (1, 0), (0, -1), (0, 1)) + + +class Solution: + def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -> List[List[int]]: + """ + dfs fill + + mistake: corner case image == new color + """ + cur_color = image[sr][sc] + if cur_color == newColor: + return image + + self.dfs(image, sr, sc, cur_color, newColor) + return image + + def dfs(self, image, i, j, cur_color, new_color): + image[i][j] = new_color + m, n = len(image), len(image[0]) + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < m and 0 <= J < n and image[I][J] == cur_color: + self.dfs(image, I, J, cur_color, new_color) From adf80782c2458efe79c345c22c421c8e87aa9bee Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 2 Mar 2019 13:31:57 -0800 Subject: [PATCH 107/344] 721 --- 721 Accounts Merge.py | 105 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 721 Accounts Merge.py diff --git a/721 Accounts Merge.py b/721 Accounts Merge.py new file mode 100644 index 0000000..e4c833e --- /dev/null +++ b/721 Accounts Merge.py @@ -0,0 +1,105 @@ +#!/usr/bin/python3 +""" +Given a list accounts, each element accounts[i] is a list of strings, where the +first element accounts[i][0] is a name, and the rest of the elements are emails +representing emails of the account. + +Now, we would like to merge these accounts. Two accounts definitely belong to +the same person if there is some email that is common to both accounts. Note +that even if two accounts have the same name, they may belong to different +people as people could have the same name. A person can have any number of +accounts initially, but all of their accounts definitely have the same name. + +After merging the accounts, return the accounts in the following format: the +first element of each account is the name, and the rest of the elements are +emails in sorted order. The accounts themselves can be returned in any order. + +Example 1: +Input: +accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", +"johnnybravo@mail.com"], ["John", "johnsmith@mail.com", +"john_newyork@mail.com"], ["Mary", "mary@mail.com"]] +Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', +'johnsmith@mail.com'], ["John", "johnnybravo@mail.com"], ["Mary", +"mary@mail.com"]] + +Explanation: +The first and third John's are the same person as they have the common email +"johnsmith@mail.com". +The second John and Mary are different people as none of their email addresses +are used by other accounts. +We could return these lists in any order, for example the answer [['Mary', +'mary@mail.com'], ['John', 'johnnybravo@mail.com'], +['John', 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com']] +would still be accepted. +Note: + +The length of accounts will be in the range [1, 1000]. +The length of accounts[i] will be in the range [1, 10]. +The length of accounts[i][j] will be in the range [1, 30]. +""" +from collections import defaultdict + + +class Solution: + def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: + """ + merge has to be dfs + account id + """ + email_to_ids = defaultdict(set) + for i, v in enumerate(accounts): + for email in v[1:]: + email_to_ids[email].add(i) + + # graph nodes by ids, edges by email + visited = [False for _ in accounts] + ret = [] + for i, v in enumerate(accounts): + if not visited[i]: + emails = set() + self.dfs(i, accounts, email_to_ids, emails, visited) + ret.append([v[0]] + sorted(emails)) + + return ret + + def dfs(self, i, accounts, email_to_ids, emails, visited): + visited[i] = True + for email in accounts[i][1:]: + emails.add(email) + for nbr in email_to_ids[email]: + if not visited[nbr]: + self.dfs(nbr, accounts, email_to_ids, emails, visited) + + + def accountsMerge_error(self, accounts: List[List[str]]) -> List[List[str]]: + """ + data structure + map: email -> id, if exist mapping, then merge + map: id -> [email] + + mistake: not dfs, search on the first level + """ + email_id = {} + id_emails = defaultdict(list) + for i in range(len(accounts)): + person = None + for email in accounts[i][1:]: + if email in email_id: + person = email_id[email] + break + + for email in accounts[i][1:]: + if person is None: + person = i + email_id[email] = person + id_emails[person].append(email) + elif email not in email_id: + email_id[email] = person + id_emails[person].append(email) + + ret = [] + for k, v in id_emails.items(): + ret.append([accounts[k][0]] + sorted(v)) + + return ret From b616bf42b6d2a2713dd8dd83e990b4ac03595582 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 2 Mar 2019 13:46:28 -0800 Subject: [PATCH 108/344] 703 --- 704 Binary Search.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 704 Binary Search.py diff --git a/704 Binary Search.py b/704 Binary Search.py new file mode 100644 index 0000000..0222a6f --- /dev/null +++ b/704 Binary Search.py @@ -0,0 +1,31 @@ +#!/usr/bin/python3 +""" +Given a sorted (in ascending order) integer array nums of n elements and a +target value, write a function to search target in nums. If target exists, then +return its index, otherwise return -1. + + +Example 1: + +Input: nums = [-1,0,3,5,9,12], target = 9 +Output: 4 +Explanation: 9 exists in nums and its index is 4 + +Example 2: + +Input: nums = [-1,0,3,5,9,12], target = 2 +Output: -1 +Explanation: 2 does not exist in nums so return -1 + + +Note: + +You may assume that all elements in nums are unique. +n will be in the range [1, 10000]. +The value of each element in nums will be in the range [-9999, 9999]. +""" +from typing import List + + +class Solution: + def search(self, nums: List[int], target: int) -> int: From bd047570a5da1dacf0fb3bf556d5e51a88662f51 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 2 Mar 2019 13:49:17 -0800 Subject: [PATCH 109/344] 703 --- 704 Binary Search.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/704 Binary Search.py b/704 Binary Search.py index 0222a6f..f759e34 100644 --- a/704 Binary Search.py +++ b/704 Binary Search.py @@ -29,3 +29,15 @@ class Solution: def search(self, nums: List[int], target: int) -> int: + lo = 0 + hi = len(nums) + while lo < hi: + mid = (lo + hi) // 2 + if nums[mid] == target: + return mid + elif nums[mid] < target: + lo = mid + 1 + else: + hi = mid + + return -1 From bb8e6a5f52a7fc965918a4d686116a69821acd9a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 2 Mar 2019 14:32:23 -0800 Subject: [PATCH 110/344] 725 --- 725 Split Linked List in Parts.py | 159 ++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 725 Split Linked List in Parts.py diff --git a/725 Split Linked List in Parts.py b/725 Split Linked List in Parts.py new file mode 100644 index 0000000..36be0d0 --- /dev/null +++ b/725 Split Linked List in Parts.py @@ -0,0 +1,159 @@ +#!/usr/bin/python3 +""" +Given a (singly) linked list with head node root, write a function to split the +linked list into k consecutive linked list "parts". + +The length of each part should be as equal as possible: no two parts should have +a size differing by more than 1. This may lead to some parts being null. + +The parts should be in order of occurrence in the input list, and parts +occurring earlier should always have a size greater than or equal parts +occurring later. + +Return a List of ListNode's representing the linked list parts that are formed. + +Examples 1->2->3->4, k = 5 // 5 equal parts [ [1], [2], [3], [4], null ] +Example 1: +Input: +root = [1, 2, 3], k = 5 +Output: [[1],[2],[3],[],[]] +Explanation: +The input and each element of the output are ListNodes, not arrays. +For example, the input root has root.val = 1, root.next.val = 2, +root.next.next.val = 3, and root.next.next.next = null. +The first element output[0] has output[0].val = 1, output[0].next = null. +The last element output[4] is null, but it's string representation as a ListNode +is []. + +Example 2: +Input: +root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3 +Output: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]] +Explanation: +The input has been split into consecutive parts with size difference at most 1, +and earlier parts are a larger size than the later parts. +Note: + +The length of root will be in the range [0, 1000]. +Each value of a node in the input will be an integer in the range [0, 999]. +k will be an integer in the range [1, 50]. +""" +# Definition for singly-linked list. +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + +import math + + +class Solution: + def splitListToParts(self, root: ListNode, k: int) -> List[ListNode]: + """ + calculate the chunk/page size at a time + + [1,2,3,4,5,6,7] + 3 + + [[1,2,3],[4,5,6],[7]] # output + [[1,2,3],[4,5],[6,7]] # expected + """ + l = 0 + node = root + while node: + l += 1 + node = node.next + + + ret = [[] for _ in range(k)] + + short_chunk_l = l // k + long_chunk_l = short_chunk_l + 1 + n_long_chunk = l % k # key + n_short_chunk = l - n_long_chunk + + chunk_counter = 0 + cur_l = 0 + node = root + while node: + ret[chunk_counter].append(node.val) + cur_l += 1 + chunk_size = long_chunk_l if chunk_counter < n_long_chunk else short_chunk_l + if cur_l == chunk_size: + cur_l = 0 + chunk_counter += 1 + node = node.next + + return ret + + def splitListToParts_2(self, root: ListNode, k: int) -> List[ListNode]: + """ + [1,2,3,4,5,6,7] + 3 + + [[1,2,3],[4,5,6],[7]] # output + [[1,2,3],[4,5],[6,7]] # expected + + need to dynamical calculate part length on the fly + """ + l = 0 + node = root + while node: + l += 1 + node = node.next + + + ret = [[] for _ in range(k)] + node = root + counter = 0 + cur_l = 0 + i = 0 + part_l = math.ceil((l - counter) / k) + while node: + cur_l += 1 + counter += 1 + ret[i].append(node.val) + if cur_l == part_l: + k -= 1 + cur_l = 0 + i += 1 + if k != 0: + part_l = math.ceil((l - counter) / k) + + node = node.next + + return ret + + def splitListToParts_error(self, root: ListNode, k: int) -> List[ListNode]: + """ + mistake, the length should be dynamically calculated + + [1,2,3,4,5,6,7] + 3 + + [[1,2,3],[4,5,6],[7]] # output + [[1,2,3],[4,5],[6,7]] # expected + """ + l = 0 + node = root + while node: + l += 1 + node = node.next + + part_l = math.ceil(l / k) + ret = [[] for _ in range(k)] + + node = root + cur_l = 0 + i = 0 + while node: + cur_l += 1 + ret[i].append(node.val) + if cur_l == part_l: + cur_l = 0 + i += 1 + + node = node.next + + return ret From ccda172a7f60bad3e5c27f8766d2d52e66ae99f2 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 2 Mar 2019 15:49:07 -0800 Subject: [PATCH 111/344] 729 --- 729 My Calendar I.py | 95 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 729 My Calendar I.py diff --git a/729 My Calendar I.py b/729 My Calendar I.py new file mode 100644 index 0000000..0b5fb4c --- /dev/null +++ b/729 My Calendar I.py @@ -0,0 +1,95 @@ +#!/usr/bin/python3 +""" +Implement a MyCalendar class to store your events. A new event can be added if +adding the event will not cause a double booking. + +Your class will have the method, book(int start, int end). Formally, this +represents a booking on the half open interval [start, end), the range of real +numbers x such that start <= x < end. + +A double booking happens when two events have some non-empty intersection (ie., +there is some time that is common to both events.) + +For each call to the method MyCalendar.book, return true if the event can be +added to the calendar successfully without causing a double booking. Otherwise, +return false and do not add the event to the calendar. + +Your class will be called like this: MyCalendar cal = new MyCalendar(); +MyCalendar.book(start, end) +Example 1: + +MyCalendar(); +MyCalendar.book(10, 20); // returns true +MyCalendar.book(15, 25); // returns false +MyCalendar.book(20, 30); // returns true +Explanation: +The first event can be booked. The second can't because time 15 is already +booked by another event. +The third event can be booked, as the first event takes every time less than 20, +but not including 20. + + +Note: + +The number of calls to MyCalendar.book per test case will be at most 1000. +In calls to MyCalendar.book(start, end), start and end are integers in the range +[0, 10^9]. +""" + + +class Node: + def __init__(self, s, e): + self.s = s + self.e = e + self.left = None + self.right = None + + +class MyCalendar: + def __init__(self): + """ + binary search + disregard + end < new.start + start > new.end + need to find + end > new.start + start < new.end + + keep sorted, list insert O(n) + need a TreeMap + but python does not have -> BST although unbalanced + """ + self.root = None + + def insert(self, node: Node, s: int, e: int) -> Node: + if not node: + return Node(s, e) + + if e <= node.s: + left = self.insert(node.left, s, e) + if left is None: + return None + node.left = left + return node + elif s >= node.e: + right = self.insert(node.right, s, e) + if right is None: + return None + node.right = right + return node + else: + return None + + def book(self, start: int, end: int) -> bool: + ret = self.insert(self.root, start, end) + if ret is None: + return False + + self.root = ret + return True + + +# Your MyCalendar object will be instantiated and called as such: +# obj = MyCalendar() +# param_1 = obj.book(start,end) From 625f902f871ef060caf38e23e90e8b76ecd6f356 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 2 Mar 2019 17:18:23 -0800 Subject: [PATCH 112/344] 731 --- 731 My Calendar II.py | 78 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 731 My Calendar II.py diff --git a/731 My Calendar II.py b/731 My Calendar II.py new file mode 100644 index 0000000..4cc2c51 --- /dev/null +++ b/731 My Calendar II.py @@ -0,0 +1,78 @@ +#!/usr/bin/python3 +""" +Implement a MyCalendarTwo class to store your events. A new event can be added +if adding the event will not cause a triple booking. + +Your class will have one method, book(int start, int end). Formally, this +represents a booking on the half open interval [start, end), the range of real +numbers x such that start <= x < end. + +A triple booking happens when three events have some non-empty intersection +(ie., there is some time that is common to all 3 events.) + +For each call to the method MyCalendar.book, return true if the event can be +added to the calendar successfully without causing a triple booking. Otherwise, +return false and do not add the event to the calendar. + +Your class will be called like this: MyCalendar cal = new MyCalendar(); +MyCalendar.book(start, end) +Example 1: + +MyCalendar(); +MyCalendar.book(10, 20); // returns true +MyCalendar.book(50, 60); // returns true +MyCalendar.book(10, 40); // returns true +MyCalendar.book(5, 15); // returns false +MyCalendar.book(5, 10); // returns true +MyCalendar.book(25, 55); // returns true +Explanation: +The first two events can be booked. The third event can be double booked. +The fourth event (5, 15) can't be booked, because it would result in a triple +booking. +The fifth event (5, 10) can be booked, as it does not use time 10 which is +already double booked. +The sixth event (25, 55) can be booked, as the time in [25, 40) will be double +booked with the third event; +the time [40, 50) will be single booked, and the time [50, 55) will be double +booked with the second event. + + +Note: + +The number of calls to MyCalendar.book per test case will be at most 1000. +In calls to MyCalendar.book(start, end), start and end are integers in the range +[0, 10^9]. +""" +import bisect + + +class MyCalendarTwo: + + def __init__(self): + """ + triple booking + boundary counting + bisect.insort + """ + self.lst = [] # can be TreeMap(), ordered map + + + def book(self, start: int, end: int) -> bool: + """ + O(lg n) + O(n) + """ + bisect.insort(self.lst, (start, "start")) + bisect.insort(self.lst, (end, "end")) + count = 0 + for _, flag in self.lst: + count += 1 if flag == "start" else -1 + if count > 2: + self.lst.remove((start, "start")) + self.lst.remove((end, "end")) + return False + + return True + +# Your MyCalendarTwo object will be instantiated and called as such: +# obj = MyCalendarTwo() +# param_1 = obj.book(start,end) From 6876535ff6ab446c373c08fe41a0b3b59ca33241 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 2 Mar 2019 17:18:31 -0800 Subject: [PATCH 113/344] 732 --- 732 My Calendar III.py | 66 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 732 My Calendar III.py diff --git a/732 My Calendar III.py b/732 My Calendar III.py new file mode 100644 index 0000000..20deae7 --- /dev/null +++ b/732 My Calendar III.py @@ -0,0 +1,66 @@ +#!/usr/bin/python3 +""" +Implement a MyCalendarThree class to store your events. A new event can always +be added. + +Your class will have one method, book(int start, int end). Formally, this +represents a booking on the half open interval [start, end), the range of real +numbers x such that start <= x < end. + +A K-booking happens when K events have some non-empty intersection (ie., there +is some time that is common to all K events.) + +For each call to the method MyCalendar.book, return an integer K representing +the largest integer such that there exists a K-booking in the calendar. + +Your class will be called like this: MyCalendarThree cal = new +MyCalendarThree(); MyCalendarThree.book(start, end) +Example 1: + +MyCalendarThree(); +MyCalendarThree.book(10, 20); // returns 1 +MyCalendarThree.book(50, 60); // returns 1 +MyCalendarThree.book(10, 40); // returns 2 +MyCalendarThree.book(5, 15); // returns 3 +MyCalendarThree.book(5, 10); // returns 3 +MyCalendarThree.book(25, 55); // returns 3 +Explanation: +The first two events can be booked and are disjoint, so the maximum K-booking +is a 1-booking. +The third event [10, 40) intersects the first event, and the maximum K-booking +is a 2-booking. +The remaining events cause the maximum K-booking to be only a 3-booking. +Note that the last event locally causes a 2-booking, but the answer is still 3 +because +eg. [10, 20), [10, 40), and [5, 15) are still triple booked. + + +Note: + +The number of calls to MyCalendarThree.book per test case will be at most 400. +In calls to MyCalendarThree.book(start, end), start and end are integers in the +range [0, 10^9]. +""" +import bisect + + +class MyCalendarThree: + + def __init__(self): + self.lst = [] + + def book(self, start: int, end: int) -> int: + bisect.insort(self.lst, (start, "start")) + bisect.insort(self.lst, (end, "end")) + ret = 0 + count = 0 + for _, flag in self.lst: + count += 1 if flag == "start" else -1 + ret = max(ret, count) + + return ret + + +# Your MyCalendarThree object will be instantiated and called as such: +# obj = MyCalendarThree() +# param_1 = obj.book(start,end) From 2fd71e9f725d6378c6cd7627b298112cc8ee7b7a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 3 Mar 2019 10:39:37 -0800 Subject: [PATCH 114/344] 739 --- 739 Daily Temperatures.py | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 739 Daily Temperatures.py diff --git a/739 Daily Temperatures.py b/739 Daily Temperatures.py new file mode 100644 index 0000000..d2c4e74 --- /dev/null +++ b/739 Daily Temperatures.py @@ -0,0 +1,44 @@ +#!/usr/bin/python3 +""" +Given a list of daily temperatures T, return a list such that, for each day in +the input, tells you how many days you would have to wait until a warmer +temperature. If there is no future day for which this is possible, put 0 instead. + +For example, given the list of temperatures T = [73, 74, 75, 71, 69, 72, 76, 73], +your output should be [1, 1, 4, 2, 1, 1, 0, 0]. + +Note: The length of temperatures will be in the range [1, 30000]. Each +temperature will be an integer in the range [30, 100]. +""" +from typing import List +from collections import deque + + +class Solution: + def dailyTemperatures(self, T: List[int]) -> List[int]: + """ + maintain a stack of monotonously decresing from right to left + (i.e. monotonously increasing from left to right) + + Why stack? because you can disregard certain non-usefull information + + scanning from right + [73, 74, 75, 71, 69, 72, 76, 73] + """ + ret = deque() + stk = [] + for i in range(len(T) - 1, -1 , -1): + while stk and T[stk[-1]] <= T[i]: # disregard smaller ones + stk.pop() + + if stk: + ret.appendleft(stk[-1] - i) + else: + ret.appendleft(0) + stk.append(i) + + return list(ret) + + +if __name__ == "__main__": + assert Solution().dailyTemperatures([73, 74, 75, 71, 69, 72, 76, 73]) == [1, 1, 4, 2, 1, 1, 0, 0] From e959472ed58bc9dcf6b841ad58073498cf559392 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 3 Mar 2019 12:04:47 -0800 Subject: [PATCH 115/344] 740 --- 740 Delete and Earn.py | 121 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 740 Delete and Earn.py diff --git a/740 Delete and Earn.py b/740 Delete and Earn.py new file mode 100644 index 0000000..bc4a040 --- /dev/null +++ b/740 Delete and Earn.py @@ -0,0 +1,121 @@ +#!/usr/bin/python3 +""" +Given an array nums of integers, you can perform operations on the array. + +In each operation, you pick any nums[i] and delete it to earn nums[i] points. +After, you must delete every element equal to nums[i] - 1 or nums[i] + 1. + +You start with 0 points. Return the maximum number of points you can earn by +applying such operations. + +Example 1: + +Input: nums = [3, 4, 2] +Output: 6 +Explanation: +Delete 4 to earn 4 points, consequently 3 is also deleted. +Then, delete 2 to earn 2 points. 6 total points are earned. + + +Example 2: + +Input: nums = [2, 2, 3, 3, 3, 4] +Output: 9 +Explanation: +Delete 3 to earn 3 points, deleting both 2's and the 4. +Then, delete 3 again to earn 3 points, and 3 again to earn 3 points. +9 total points are earned. + + +Note: + +The length of nums is at most 20000. +Each element nums[i] is an integer in the range [1, 10000]. +""" +from typing import List +from collections import defaultdict + + +class Solution: + def deleteAndEarn(self, nums: List[int]) -> int: + """ + reduce to house rob problem + whether to pick the number or not + F[n] = max + F[n-1] if not pick n + F[n-2] + reward if pick n + + """ + rewards = [0 for _ in range(10001)] + for num in nums: + rewards[num] += num + + # whether to pick the number or not + cur, prev = 0, 0 + for reward in rewards: + nxt = max(cur, prev + reward) + prev = cur + cur = nxt + + return cur + + def deleteAndEarn_dp(self, nums: List[int]) -> int: + """ + reduce to house rob problem + whether to pick the number or not + F[n] = max + F[n-1] if not pick n + F[n-2] + reward if pick n + + """ + counter = defaultdict(int) + for n in nums: + counter[n] += 1 + + F = [0 for _ in range(10000 + 3)] + for i in range(3, 10000 + 3): + cur = i - 2 + F[i] = max( + F[i-1], + F[i-2] + counter[cur] * cur + ) + return F[-1] + + def deleteAndEarn_slow(self, nums: List[int]) -> int: + """ + geedy + dp: chose to delete max or max - 1 + O(n lg n) + + O(n^2) + """ + nums.sort() + # transform to (num, count) + counter = [] + i = 0 + j = 0 + while i < len(nums): + while j < len(nums) and nums[i] == nums[j]: + j += 1 + counter.append((nums[i], j - i)) + i = j + + # F[i] be the max points delete counter[i] + F = [0 for _ in counter] + for i in range(len(counter)): + F[i] = counter[i][0] * counter[i][1] + F[i] += max( + [ + F[j] + for j in range(i) + if counter[j][0] != counter[i][0] - 1 + ] + or [0] + ) + + return max(F or [0]) + + +if __name__ == "__main__": + assert Solution().deleteAndEarn([1,1,1,2,4,5,5,5,6]) == 18 + assert Solution().deleteAndEarn([3, 4, 2]) == 6 + assert Solution().deleteAndEarn([2, 2, 3, 3, 3, 4]) == 9 From be5747ae1b1e46addf18fd608efb02c38cf8591a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 3 Mar 2019 15:44:28 -0800 Subject: [PATCH 116/344] 735 --- 735 Asteroid Collision.py | 111 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 735 Asteroid Collision.py diff --git a/735 Asteroid Collision.py b/735 Asteroid Collision.py new file mode 100644 index 0000000..02a5399 --- /dev/null +++ b/735 Asteroid Collision.py @@ -0,0 +1,111 @@ +#!/usr/bin/python3 +""" +We are given an array asteroids of integers representing asteroids in a row. + +For each asteroid, the absolute value represents its size, and the sign +represents its direction (positive meaning right, negative meaning left). Each +asteroid moves at the same speed. + +Find out the state of the asteroids after all collisions. If two asteroids meet, +the smaller one will explode. If both are the same size, both will explode. Two +asteroids moving in the same direction will never meet. + +Example 1: +Input: +asteroids = [5, 10, -5] +Output: [5, 10] +Explanation: +The 10 and -5 collide resulting in 10. The 5 and 10 never collide. +Example 2: +Input: +asteroids = [8, -8] +Output: [] +Explanation: +The 8 and -8 collide exploding each other. +Example 3: +Input: +asteroids = [10, 2, -5] +Output: [10] +Explanation: +The 2 and -5 collide resulting in -5. The 10 and -5 collide resulting in 10. +Example 4: +Input: +asteroids = [-2, -1, 1, 2] +Output: [-2, -1, 1, 2] +Explanation: +The -2 and -1 are moving left, while the 1 and 2 are moving right. +Asteroids moving the same direction never meet, so no asteroids will meet each +other. +Note: + +The length of asteroids will be at most 10000. +Each asteroid will be a non-zero integer in the range [-1000, 1000].. +""" +from typing import List + + +class Solution: + def asteroidCollision(self, asteroids: List[int]) -> List[int]: + """ + simplified + + while-else: break statement break the entire while-else block + for-else: break statement break the entire for-else block + + stack from left to right, only -> <- will pop the stack + """ + stk = [] + for e in asteroids: + while stk and e < 0 < stk[-1]: + if abs(e) > abs(stk[-1]): + # -> exploded, <- continues + stk.pop() + elif abs(e) == abs(stk[-1]): + # -> <- both exploded + stk.pop() + break + else: + # <- exploded, -> continue + break + else: + stk.append(e) + + return stk + + def asteroidCollision_complex(self, asteroids: List[int]) -> List[int]: + """ + asteroids same speed + list of size + + stk of index + + only -> <- will collide + """ + stk = [] + n = len(asteroids) + for i in range(n-1, -1, -1): + cur = asteroids[i] + while stk and asteroids[stk[-1]] < 0 and cur > 0 and abs(asteroids[stk[-1]]) < abs(cur): + stk.pop() + + if stk and cur > 0 and asteroids[stk[-1]] == -cur: + stk.pop() + continue + + if not stk: + stk.append(i) + continue + + if not (asteroids[stk[-1]] < 0 and cur > 0) or abs(cur) > abs(asteroids[stk[-1]]): + stk.append(i) + + return [ + asteroids[i] + for i in stk[::-1] + ] + + +if __name__ == "__main__": + assert Solution().asteroidCollision([10, 2, -5]) == [10] + assert Solution().asteroidCollision([5, 10, -5]) == [5, 10] + assert Solution().asteroidCollision([8, -8]) == [] From d18618e34c5e513745cfeec3b94c4978a0296429 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 3 Mar 2019 16:35:47 -0800 Subject: [PATCH 117/344] 743 --- 743 Network Delay Time.py | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 743 Network Delay Time.py diff --git a/743 Network Delay Time.py b/743 Network Delay Time.py new file mode 100644 index 0000000..7f759ff --- /dev/null +++ b/743 Network Delay Time.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 +""" +There are N network nodes, labelled 1 to N. + +Given times, a list of travel times as directed edges times[i] = (u, v, w), +where u is the source node, v is the target node, and w is the time it takes for +a signal to travel from source to target. + +Now, we send a signal from a certain node K. How long will it take for all nodes +to receive the signal? If it is impossible, return -1. + +Note: + +N will be in the range [1, 100]. +K will be in the range [1, N]. +The length of times will be in the range [1, 6000]. +All edges times[i] = (u, v, w) will have 1 <= u, v <= N and 0 <= w <= 100. +""" +from typing import List +from collections import defaultdict +import heapq + + +class Solution: + def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int: + """ + Dijkstra's algorithm + """ + G = defaultdict(dict) + reach_time = [float('inf') for _ in range(N + 1)] + for u, v, w in times: + G[u][v] = w + + h = [(0, K)] + reach_time[K] = 0 + while h: + t, s = heapq.heappop(h) + if s in G: + for d, w in G[s].items(): + if t + w < reach_time[d]: + reach_time[d] = t + w + heapq.heappush(h, (t + w, d)) + + ret = max(reach_time[1:]) # notice reach_time[0] is dummy + if ret == float('inf'): + return -1 + return ret + + +if __name__ == "__main__": + assert Solution().networkDelayTime([[2,1,1],[2,3,1],[3,4,1]], 4, 2) == 2 From 10a4cc7c6a9c4b340bda0d1cbe2f3fb2d78737d8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 3 Mar 2019 17:17:35 -0800 Subject: [PATCH 118/344] 738 --- 738 Monotone Increasing Digits.py | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 738 Monotone Increasing Digits.py diff --git a/738 Monotone Increasing Digits.py b/738 Monotone Increasing Digits.py new file mode 100644 index 0000000..89888a4 --- /dev/null +++ b/738 Monotone Increasing Digits.py @@ -0,0 +1,46 @@ +#!/usr/bin/python3 +""" +Given a non-negative integer N, find the largest number that is less than or +equal to N with monotone increasing digits. + +(Recall that an integer has monotone increasing digits if and only if each pair +of adjacent digits x and y satisfy x <= y.) + +Example 1: +Input: N = 10 +Output: 9 +Example 2: +Input: N = 1234 +Output: 1234 +Example 3: +Input: N = 332 +Output: 299 +Note: N is an integer in the range [0, 10^9]. +""" + + +class Solution: + def monotoneIncreasingDigits(self, N: int) -> int: + """ + 332 + 322 + 222 + fill 9 + 299 + """ + digits = [int(e) for e in str(N)] + pointer = len(digits) + for i in range(len(digits) - 1, 0, -1): + if digits[i - 1] > digits[i]: + pointer = i + digits[i - 1] -= 1 + + for i in range(pointer, len(digits)): + digits[i] = 9 + + return int("".join(map(str, digits))) + + +if __name__ == "__main__": + assert Solution().monotoneIncreasingDigits(10) == 9 + assert Solution().monotoneIncreasingDigits(332) == 299 From 2e5e6047be6ec96eb56895f05c77eb84a611d7c7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 3 Mar 2019 18:44:05 -0800 Subject: [PATCH 119/344] 746 --- 746 Min Cost Climbing Stairs.py | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 746 Min Cost Climbing Stairs.py diff --git a/746 Min Cost Climbing Stairs.py b/746 Min Cost Climbing Stairs.py new file mode 100644 index 0000000..792a61e --- /dev/null +++ b/746 Min Cost Climbing Stairs.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" +On a staircase, the i-th step has some non-negative cost cost[i] assigned +(0 indexed). + +Once you pay the cost, you can either climb one or two steps. You need to find +minimum cost to reach the top of the floor, and you can either start from the +step with index 0, or the step with index 1. + +Example 1: +Input: cost = [10, 15, 20] +Output: 15 +Explanation: Cheapest is start on cost[1], pay that cost and go to the top. +Example 2: +Input: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] +Output: 6 +Explanation: Cheapest is start on cost[0], and only step on 1s, skipping +cost[3]. +Note: +cost will have a length in the range [2, 1000]. +Every cost[i] will be an integer in the range [0, 999]. +""" +from typing import List + + +class Solution: + def minCostClimbingStairs(self, cost: List[int]) -> int: + """ + dp + let F[i] be the cost to reach i-th stair + F[i] = min + F[i-2] + cost[i-2] + F[i-1] + cost[i-1] + """ + n = len(cost) + F = [float('inf') for _ in range(n+1)] + F[0] = 0 + F[1] = 0 + for i in range(2, n+1): + F[i] = min( + F[i-2] + cost[i-2], + F[i-1] + cost[i-1] + ) + + return F[-1] + + +if __name__ == "__main__": + assert Solution().minCostClimbingStairs([10, 15, 20]) == 15 From 0b99aba335e7f419cbae46b68a93fe5ae0a53043 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 3 Mar 2019 20:03:57 -0800 Subject: [PATCH 120/344] 754 --- 754 Reach a Number.py | 54 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 754 Reach a Number.py diff --git a/754 Reach a Number.py b/754 Reach a Number.py new file mode 100644 index 0000000..0769d80 --- /dev/null +++ b/754 Reach a Number.py @@ -0,0 +1,54 @@ +#!/usr/bin/python3 +""" +You are standing at position 0 on an infinite number line. There is a goal at +position target. + +On each move, you can either go left or right. During the n-th move (starting +from 1), you take n steps. + +Return the minimum number of steps required to reach the destination. + +Example 1: +Input: target = 3 +Output: 2 +Explanation: +On the first move we step from 0 to 1. +On the second step we step from 1 to 3. +Example 2: +Input: target = 2 +Output: 3 +Explanation: +On the first move we step from 0 to 1. +On the second move we step from 1 to -1. +On the third move we step from -1 to 2. +Note: +target will be a non-zero integer in the range [-10^9, 10^9]. +""" + + +class Solution: + def reachNumber(self, target: int) -> int: + """ + math + + put -/+ for 1, 2, 3, 4, ..., k + flip a sign change in even number + + if target negative, flip the sign. Thus, we can only consider positive + number + """ + target = abs(target) + s = 0 + k = 0 + while s < target: + k += 1 + s += k + + delta = s - target + if delta % 2 == 0: + return k + else: # delta is odd + if (k + 1) % 2 == 1: + return k + 1 + else: + return k + 2 From 47b8c3370b44831df87263537d5ec2816dda6c9c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Mar 2019 23:04:52 -0800 Subject: [PATCH 121/344] 146 --- 146 LRU Cache py3.py | 93 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 146 LRU Cache py3.py diff --git a/146 LRU Cache py3.py b/146 LRU Cache py3.py new file mode 100644 index 0000000..99bc489 --- /dev/null +++ b/146 LRU Cache py3.py @@ -0,0 +1,93 @@ +#!/usr/bin/python3 +""" +Design and implement a data structure for Least Recently Used (LRU) cache. It +should support the following operations: get and put. + +get(key) - Get the value (will always be positive) of the key if the key exists +in the cache, otherwise return -1. +put(key, value) - Set or insert the value if the key is not already present. +When the cache reached its capacity, it should invalidate the least recently +used item before inserting a new item. + +Follow up: +Could you do both operations in O(1) time complexity? + +Example: + +LRUCache cache = new LRUCache( 2 /* capacity */ ); + +cache.put(1, 1); +cache.put(2, 2); +cache.get(1); // returns 1 +cache.put(3, 3); // evicts key 2 +cache.get(2); // returns -1 (not found) +cache.put(4, 4); // evicts key 1 +cache.get(1); // returns -1 (not found) +cache.get(3); // returns 3 +cache.get(4); // returns 4 +""" + + +class Node: + def __init__(self, key, val): + self.key = key + self.val = val + self.prev, self.next = None, None + + +class LRUCache: + + def __init__(self, capacity: int): + """ + O(1) look up - Map + O(1) update most recent vs. least recent - Linked List + But Single linked list is not enough then Double Linked List + + Need dummy head and tail to avoid over complication of null checking + """ + self.head = Node(None, None) + self.tail = Node(None, None) + self.head.next = self.tail + self.tail.prev = self.head + self.cap = capacity + self.map = {} + + def get(self, key: int) -> int: + if key in self.map: + node = self.map[key] + self._remove(key) + self._appendleft(node) + return node.val + + return -1 + + def put(self, key: int, value: int) -> None: + if key in self.map: + self._remove(key) + elif len(self.map) >= self.cap: + node = self.tail.prev + self._remove(node.key) + + node = Node(key, value) + self._appendleft(node) + + def _appendleft(self, node: Node): + self.map[node.key] = node # update/delete map in these two operators + nxt = self.head.next + self.head.next = node + node.prev = self.head + node.next = nxt + nxt.prev = node + + def _remove(self, key: int): + node = self.map[key] + prev = node.prev + nxt = node.next + prev.next = nxt + nxt.prev = prev + del self.map[key] # update/delete map in these two operators + +# Your LRUCache object will be instantiated and called as such: +# obj = LRUCache(capacity) +# param_1 = obj.get(key) +# obj.put(key,value) From aa69c63ba615036949276e09555a6f9b65f22c39 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Mar 2019 23:55:10 -0800 Subject: [PATCH 122/344] 206 --- 206 Reverse Linked List py3.py | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 206 Reverse Linked List py3.py diff --git a/206 Reverse Linked List py3.py b/206 Reverse Linked List py3.py new file mode 100644 index 0000000..b7e607a --- /dev/null +++ b/206 Reverse Linked List py3.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +""" +Reverse a singly linked list. + +Example: + +Input: 1->2->3->4->5->NULL +Output: 5->4->3->2->1->NULL +Follow up: + +A linked list can be reversed either iteratively or recursively. Could you +implement both? +""" + + +# Definition for singly-linked list. +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + +class Solution: + def reverseList(self, head: ListNode) -> ListNode: + prev = None + cur = head + while cur: + nxt = cur.next + cur.next = prev + + prev = cur + cur = nxt + + return prev + + def reverseList_complex(self, head: ListNode) -> ListNode: + if not head: + return None + + prev = head + cur = head.next + head.next = None + while prev and cur: + nxt = cur.next + cur.next = prev + + prev = cur + cur = nxt + + return prev From 37d722ccc23a02a01b0775bed435ef4169d808ee Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 6 Mar 2019 00:27:48 -0800 Subject: [PATCH 123/344] 092 --- 092 Reverse Linked List II py3.py | 53 +++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 092 Reverse Linked List II py3.py diff --git a/092 Reverse Linked List II py3.py b/092 Reverse Linked List II py3.py new file mode 100644 index 0000000..6c3e7e2 --- /dev/null +++ b/092 Reverse Linked List II py3.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +""" +Reverse a linked list from position m to n. Do it in one-pass. + +Note: 1 ≤ m ≤ n ≤ length of list. + +Example: + +Input: 1->2->3->4->5->NULL, m = 2, n = 4 +Output: 1->4->3->2->5->NULL +""" + + +# Definition for singly-linked list. +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + +class Solution: + def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode: + prev = None + cur = head + + l = 1 + while l < m: + nxt = cur.next + prev = cur + cur = nxt + l += 1 + # prev cur (m) + # -> ... -> left -> right -> ... -> null + # (n) + # -> ... -> left <- right <- ... <- prev <- cur -> ... -> null + # -> ... -> left -> prev -> ... -> right -> cur -> ... -> null + leftend = prev + rightend = cur + + while l <= n: # notice is it <= + nxt = cur.next + cur.next = prev + prev = cur + cur = nxt + l += 1 + + if m != 1: # leftend is None + leftend.next = prev + else: + head = prev + + rightend.next = cur + return head From c6a8e75fe7e13cd5da3037bc92b5446bc41783c5 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 7 Mar 2019 21:16:16 -0800 Subject: [PATCH 124/344] 235 236 --- ...on Ancestor of a Binary Search Tree py3.py | 51 ++++++++++++++++ ...st Common Ancestor of a Binary Tree py3.py | 60 +++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 235 Lowest Common Ancestor of a Binary Search Tree py3.py create mode 100644 236 Lowest Common Ancestor of a Binary Tree py3.py diff --git a/235 Lowest Common Ancestor of a Binary Search Tree py3.py b/235 Lowest Common Ancestor of a Binary Search Tree py3.py new file mode 100644 index 0000000..a00607d --- /dev/null +++ b/235 Lowest Common Ancestor of a Binary Search Tree py3.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 +""" +Given a binary search tree (BST), find the lowest common ancestor (LCA) of two +given nodes in the BST. + +According to the definition of LCA on Wikipedia: “The lowest common ancestor is +defined between two nodes p and q as the lowest node in T that has both p and q +as descendants (where we allow a node to be a descendant of itself).” + +Given binary search tree: root = [6,2,8,0,4,7,9,null,null,3,5] + + + + +Example 1: + +Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 +Output: 6 +Explanation: The LCA of nodes 2 and 8 is 6. +Example 2: + +Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4 +Output: 2 +Explanation: The LCA of nodes 2 and 4 is 2, since a node can be a descendant of +itself according to the LCA definition. + +Note: + +All of the nodes' values will be unique. +p and q are different and both values will exist in the BST. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode: + return self.walk(root, p, q) + + def walk(self, node, p, q): + if p.val > node.val and q.val > node.val: + return self.walk(node.right, p, q) + if p.val < node.val and q.val < node.val: + return self.walk(node.left, p, q) + return node diff --git a/236 Lowest Common Ancestor of a Binary Tree py3.py b/236 Lowest Common Ancestor of a Binary Tree py3.py new file mode 100644 index 0000000..d0a0f32 --- /dev/null +++ b/236 Lowest Common Ancestor of a Binary Tree py3.py @@ -0,0 +1,60 @@ +#!/usr/bin/python3 +""" +Given a binary tree, find the lowest common ancestor (LCA) of two given nodes +in the tree. + +According to the definition of LCA on Wikipedia: “The lowest common ancestor is +defined between two nodes p and q as the lowest node in T that has both p and q +as descendants (where we allow a node to be a descendant of itself).” + +Given the following binary tree: root = [3,5,1,6,2,0,8,null,null,7,4] + + + + +Example 1: + +Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 +Output: 3 +Explanation: The LCA of nodes 5 and 1 is 3. +Example 2: + +Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 +Output: 5 +Explanation: The LCA of nodes 5 and 4 is 5, since a node can be a descendant of +itself according to the LCA definition. + +Note: + +All of the nodes' values will be unique. +p and q are different and both values will exist in the binary tree. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.ans = None + + def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode: + self.count(root, p, q) + return self.ans + + def count(self, node, p, q): + if not node: + return 0 + + lcount = self.count(node.left, p, q) + rcount = self.count(node.right, p, q) + mcount = 1 if node == p or node == q else 0 + ret = lcount + rcount + mcount + if lcount == 1 and rcount == 1 or lcount == 1 and mcount == 1 or rcount == 1 and mcount == 1: + self.ans = node + return ret From c9193a1257252e796a9fcb0e1ad600f492e68312 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 9 Mar 2019 14:29:01 -0800 Subject: [PATCH 125/344] 334 --- 334 Increasing Triplet Subsequence py3.py | 38 +++++++++++++++++++++++ 334 Increasing Triplet Subsequence.py | 2 ++ 2 files changed, 40 insertions(+) create mode 100644 334 Increasing Triplet Subsequence py3.py diff --git a/334 Increasing Triplet Subsequence py3.py b/334 Increasing Triplet Subsequence py3.py new file mode 100644 index 0000000..2645765 --- /dev/null +++ b/334 Increasing Triplet Subsequence py3.py @@ -0,0 +1,38 @@ +#!/usr/bin/python3 +""" +Given an unsorted array return whether an increasing subsequence of length 3 +exists or not in the array. + +Formally the function should: + +Return true if there exists i, j, k +such that arr[i] < arr[j] < arr[k] given 0 ≤ i < j < k ≤ n-1 else return false. +Note: Your algorithm should run in O(n) time complexity and O(1) space complexity. + +Example 1: + +Input: [1,2,3,4,5] +Output: true +Example 2: + +Input: [5,4,3,2,1] +Output: false +""" +from typing import List +from bisect import bisect_left + + +class Solution: + def increasingTriplet(self, nums: List[int]) -> bool: + """ + Patience sort + LIS dp with binary search + """ + F = [float('inf') for _ in range(3)] + for n in nums: + i = bisect_left(F, n) + if i >= 2: + return True + F[i] = n + + return False diff --git a/334 Increasing Triplet Subsequence.py b/334 Increasing Triplet Subsequence.py index 2a26933..6bd9611 100644 --- a/334 Increasing Triplet Subsequence.py +++ b/334 Increasing Triplet Subsequence.py @@ -22,6 +22,8 @@ class Solution(object): def increasingTriplet(self, nums): """ Brute force: O(N^3) + + dp: O(N) :type nums: List[int] :rtype: bool """ From 27f92a19103be1a5322bfd4db94a558517ebb386 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 9 Mar 2019 14:30:03 -0800 Subject: [PATCH 126/344] 300 --- 300 Longest Increasing Subsequence py3.py | 41 +++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 300 Longest Increasing Subsequence py3.py diff --git a/300 Longest Increasing Subsequence py3.py b/300 Longest Increasing Subsequence py3.py new file mode 100644 index 0000000..f587209 --- /dev/null +++ b/300 Longest Increasing Subsequence py3.py @@ -0,0 +1,41 @@ +#!/usr/bin/python3 +""" +Given an unsorted array of integers, find the length of longest increasing +subsequence. + +Example: + +Input: [10,9,2,5,3,7,101,18] +Output: 4 +Explanation: The longest increasing subsequence is [2,3,7,101], therefore the +length is 4. +Note: + +There may be more than one LIS combination, it is only necessary for you to +return the length. +Your algorithm should run in O(n2) complexity. +Follow up: Could you improve it to O(n log n) time complexity? +""" +from typing import List +from bisect import bisect_left + + +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + """ + LIS dp + binary search + Patience sort + F maintains a increasing array + F[i] stores the position of A[j] in the F + + For easch position i, the F[i]_old > F[i]_new, but A[j_old] < A[j_new] + + """ + F = [float('inf') for _ in nums] + l = 0 + for n in nums: + i = bisect_left(F, n) # consider equal elements [2, 2] + F[i] = n + l = max(l, i + 1) + + return l From c30ce9d729322567caac9429e9308ad9aaeeb942 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 9 Mar 2019 15:46:51 -0800 Subject: [PATCH 127/344] 763 --- 763 Partition Labels py3.py | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 763 Partition Labels py3.py diff --git a/763 Partition Labels py3.py b/763 Partition Labels py3.py new file mode 100644 index 0000000..b947b3f --- /dev/null +++ b/763 Partition Labels py3.py @@ -0,0 +1,45 @@ +#!/usr/bin/python3 +""" +A string S of lowercase letters is given. We want to partition this string into +as many parts as possible so that each letter appears in at most one part, and +return a list of integers representing the size of these parts. + +Example 1: +Input: S = "ababcbacadefegdehijhklij" +Output: [9,7,8] +Explanation: +The partition is "ababcbaca", "defegde", "hijhklij". +This is a partition so that each letter appears in at most one part. +A partition like "ababcbacadefegde", "hijhklij" is incorrect, because it splits +S into less parts. +Note: + +S will have length in range [1, 500]. +S will consist of lowercase letters ('a' to 'z') only. +""" +from typing import List + + +class Solution: + def partitionLabels(self, S: str) -> List[int]: + lasts = {} + n = len(S) + for i in range(n-1, -1, -1): + if S[i] not in lasts: + lasts[S[i]] = i + + indexes = [-1] # last partition ending index + cur_last = 0 + for i in range(n): + cur_last = max(cur_last, lasts[S[i]]) + if cur_last == i: + indexes.append(cur_last) + + ret = [] + for i in range(len(indexes) - 1): + ret.append(indexes[i+1] - indexes[i]) + return ret + + +if __name__ == "__main__": + assert Solution().partitionLabels("ababcbacadefegdehijhklij") == [9, 7, 8] From 13658c9d5e48bfd5c0cacfc5686263eefab81d7c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 9 Mar 2019 16:38:31 -0800 Subject: [PATCH 128/344] 787 --- 787 Cheapest Flights Within K Stops py3.py | 69 ++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 787 Cheapest Flights Within K Stops py3.py diff --git a/787 Cheapest Flights Within K Stops py3.py b/787 Cheapest Flights Within K Stops py3.py new file mode 100644 index 0000000..d0e4826 --- /dev/null +++ b/787 Cheapest Flights Within K Stops py3.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +""" +There are n cities connected by m flights. Each fight starts from city u and +arrives at v with a price w. + +Now given all the cities and flights, together with starting city src and the +destination dst, your task is to find the cheapest price from src to dst with up +to k stops. If there is no such route, output -1. + +Example 1: +Input: +n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]] +src = 0, dst = 2, k = 1 +Output: 200 +Explanation: +The graph looks like this: + + +The cheapest price from city 0 to city 2 with at most 1 stop costs 200, as +marked red in the picture. +Example 2: +Input: +n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]] +src = 0, dst = 2, k = 0 +Output: 500 +Explanation: +The graph looks like this: + + +The cheapest price from city 0 to city 2 with at most 0 stop costs 500, as +marked blue in the picture. +Note: + +The number of nodes n will be in range [1, 100], with nodes labeled from 0 to +n - 1. + +The size of flights will be in range [0, n * (n - 1) / 2]. +The format of each flight will be (src, dst, price). +The price of each flight will be in the range [1, 10000]. +k is in the range of [0, n - 1]. +There will not be any duplicated flights or self cycles. +""" +from collections import defaultdict +import heapq + + +class Solution: + def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, K: int) -> int: + """ + dijkstra + """ + G = defaultdict(dict) + visited = defaultdict(bool) + for u, v, w in flights: + G[u][v] = w + + pq = [(0, 0, src)] # (cost, step, city) + while pq: + cost, k, u = heapq.heappop(pq) + if u == dst: + return cost + + stops = k - 1 + 1 + if stops <= K: + for v, w in G[u].items(): + heapq.heappush(pq, (cost + w, k + 1, v)) + + + return -1 From 413f804677cb4e6d758818636225d1264c6b35ad Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 10 Mar 2019 14:56:28 -0700 Subject: [PATCH 129/344] 779 --- 779 K-th Symbol in Grammar.py | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 779 K-th Symbol in Grammar.py diff --git a/779 K-th Symbol in Grammar.py b/779 K-th Symbol in Grammar.py new file mode 100644 index 0000000..e8b0d8a --- /dev/null +++ b/779 K-th Symbol in Grammar.py @@ -0,0 +1,82 @@ +#!/usr/bin/python3 +""" +On the first row, we write a 0. Now in every subsequent row, we look at the +previous row and replace each occurrence of 0 with 01, and each occurrence of 1 +with 10. + +Given row N and index K, return the K-th indexed symbol in row N. (The values of +K are 1-indexed.) (1 indexed). + +Examples: +Input: N = 1, K = 1 +Output: 0 + +Input: N = 2, K = 1 +Output: 0 + +Input: N = 2, K = 2 +Output: 1 + +Input: N = 4, K = 5 +Output: 1 + +Explanation: +row 1: 0 +row 2: 01 +row 3: 0110 +row 4: 01101001 +Note: + +N will be an integer in the range [1, 30]. +K will be an integer in the range [1, 2^(N-1)]. +""" + + +class Solution: + def kthGrammar(self, N: int, K: int) -> int: + """ + pattern + + 0 + 0 1 + 01 10 + 0110 1001 + recursive go thorugh the pattern + """ + return self.dfs(N, K, True) + + def dfs(self, N, K, not_flip): + if N == 1: + return 0 if not_flip else 1 + half_l = 2 ** (N - 1) // 2 + if K <= half_l: + return self.dfs(N - 1, K, not_flip) + else: + return self.dfs(N - 1, K - half_l, not not_flip) + + def kthGrammar_TLE(self, N: int, K: int) -> int: + """ + Find pattern + Precedence: Logic < Bitwise < Arithmetic operator < Unary + + 0 + 0 1 + 01 10 + 0110 1001 + Generating the actual string will TLE + """ + row = 0 + pos = 1 + for n in range(1, N): + row = (row << pos) + (~row & 2 ** pos - 1) + pos *= 2 + + ret = row >> pos - K & 1 + return ret + + +if __name__ == "__main__": + assert Solution().kthGrammar(1, 1) == 0 + assert Solution().kthGrammar(2, 1) == 0 + assert Solution().kthGrammar(2, 2) == 1 + assert Solution().kthGrammar(4, 5) == 1 From 23a304db9b655cce8f14b160ec391df6a10a1b3e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 10 Mar 2019 15:30:10 -0700 Subject: [PATCH 130/344] 764 --- 764 Largest Plus Sign.py | 105 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 764 Largest Plus Sign.py diff --git a/764 Largest Plus Sign.py b/764 Largest Plus Sign.py new file mode 100644 index 0000000..acefa12 --- /dev/null +++ b/764 Largest Plus Sign.py @@ -0,0 +1,105 @@ +#!/usr/bin/python3 +""" +In a 2D grid from (0, 0) to (N-1, N-1), every cell contains a 1, except those +cells in the given list mines which are 0. What is the largest axis-aligned plus +sign of 1s contained in the grid? Return the order of the plus sign. If there is +none, return 0. + +An "axis-aligned plus sign of 1s of order k" has some center grid[x][y] = 1 +along with 4 arms of length k-1 going up, down, left, and right, and made of 1s. +This is demonstrated in the diagrams below. Note that there could be 0s or 1s +beyond the arms of the plus sign, only the relevant area of the plus sign is +checked for 1s. + +Examples of Axis-Aligned Plus Signs of Order k: + +Order 1: +000 +010 +000 + +Order 2: +00000 +00100 +01110 +00100 +00000 + +Order 3: +0000000 +0001000 +0001000 +0111110 +0001000 +0001000 +0000000 +Example 1: + +Input: N = 5, mines = [[4, 2]] +Output: 2 +Explanation: +11111 +11111 +11111 +11111 +11011 +In the above grid, the largest plus sign can only be order 2. One of them is +marked in bold. +Example 2: + +Input: N = 2, mines = [] +Output: 1 +Explanation: +There is no plus sign of order 2, but there is of order 1. +Example 3: + +Input: N = 1, mines = [[0, 0]] +Output: 0 +Explanation: +There is no plus sign, so return 0. +Note: + +N will be an integer in the range [1, 500]. +mines will have length at most 5000. +mines[i] will be length 2 and consist of integers in the range [0, N-1]. +(Additionally, programs submitted in C, C++, or C# will be judged with a +slightly smaller time limit.) +""" +from typing import List + + +class Solution: + def orderOfLargestPlusSign(self, N: int, mines: List[List[int]]) -> int: + """ + < ^ > V four directions + Let F[i][j][k] be the number of consecutive 1 including G[i][j] it self + """ + G = [[1 for _ in range(N)] for _ in range(N)] + for i, j in mines: + G[i][j] = 0 + + F = [[[G[i][j] for _ in range(4)] for j in range(N)] for i in range(N)] + for i in range(N): + for j in range(N): + if j - 1 >= 0 and G[i][j] == 1: + F[i][j][0] = F[i][j-1][0] + 1 + if i - 1 >= 0 and G[i][j] == 1: + F[i][j][1] = F[i-1][j][1] + 1 + + for i in range(N-1, -1, -1): + for j in range(N-1, -1, -1): + if j + 1 < N and G[i][j] == 1: + F[i][j][2] = F[i][j+1][2] + 1 + if i + 1 < N and G[i][j] == 1: + F[i][j][3] = F[i+1][j][3] + 1 + + ret = 0 + for i in range(N): + for j in range(N): + ret = max(ret, min(F[i][j])) + + return ret + + +if __name__ == "__main__": + assert Solution().orderOfLargestPlusSign(5, [[4, 2]]) == 2 From 2690c998d5caeac6a3ceadfac56b4a3d4e0e4105 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 10 Mar 2019 19:49:33 -0700 Subject: [PATCH 131/344] 766 --- 766 Toeplitz Matrix.py | 87 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 766 Toeplitz Matrix.py diff --git a/766 Toeplitz Matrix.py b/766 Toeplitz Matrix.py new file mode 100644 index 0000000..5836530 --- /dev/null +++ b/766 Toeplitz Matrix.py @@ -0,0 +1,87 @@ +#!/usr/bin/python3 +""" +A matrix is Toeplitz if every diagonal from top-left to bottom-right has the +same element. + +Now given an M x N matrix, return True if and only if the matrix is Toeplitz. + + +Example 1: + +Input: +matrix = [ + [1,2,3,4], + [5,1,2,3], + [9,5,1,2] +] +Output: True +Explanation: +In the above grid, the diagonals are: +"[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]". +In each diagonal all elements are the same, so the answer is True. +Example 2: + +Input: +matrix = [ + [1,2], + [2,2] +] +Output: False +Explanation: +The diagonal "[1, 2]" has different elements. + +Note: + +matrix will be a 2D array of integers. +matrix will have a number of rows and columns in range [1, 20]. +matrix[i][j] will be integers in range [0, 99]. + +Follow up: + +What if the matrix is stored on disk, and the memory is limited such that you +can only load at most one row of the matrix into the memory at once? +What if the matrix is so large that you can only load up a partial row into the +memory at once? +""" +from typing import List + + +class Solution: + def isToeplitzMatrix(self, matrix: List[List[int]]) -> bool: + m, n = len(matrix), len(matrix[0]) + for i in range(1, m): + for j in range(1, n): + if matrix[i][j] != matrix[i-1][j-1]: + return False + + return True + + def isToeplitzMatrix_complex(self, matrix: List[List[int]]) -> bool: + """ + Brute force iteration will work + + need a good way to go through the matrix + """ + m, n = len(matrix), len(matrix[0]) + for j in range(n): + r = 0 + c = j + cur = matrix[r][c] + while r < m and c < n: + if cur != matrix[r][c]: + return False + r += 1 + c += 1 + + for i in range(1, m): + r = i + c = 0 + cur = matrix[r][c] + while r < m and c < n: + if cur != matrix[r][c]: + return False + + r += 1 + c += 1 + + return True From 8cf695fd07497dfac0d10a0fdd0a12e4dbf08fdf Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 10 Mar 2019 20:12:03 -0700 Subject: [PATCH 132/344] 771 --- 771 Jewels and Stones.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 771 Jewels and Stones.py diff --git a/771 Jewels and Stones.py b/771 Jewels and Stones.py new file mode 100644 index 0000000..be29c81 --- /dev/null +++ b/771 Jewels and Stones.py @@ -0,0 +1,33 @@ +#!/usr/bin/python3 +""" +You're given strings J representing the types of stones that are jewels, and S +representing the stones you have. Each character in S is a type of stone you +have. You want to know how many of the stones you have are also jewels. + +The letters in J are guaranteed distinct, and all characters in J and S are +letters. Letters are case sensitive, so "a" is considered a different type of +stone from "A". + +Example 1: + +Input: J = "aA", S = "aAAbbbb" +Output: 3 +Example 2: + +Input: J = "z", S = "ZZ" +Output: 0 +""" + + +class Solution: + def numJewelsInStones(self, J: str, S: str) -> int: + """ + hash map + """ + targets = set(J) + ret = 0 + for c in S: + if c in targets: + ret += 1 + + return ret From b86794ad870580773633d4d7afc1b357b4af9b28 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 10 Mar 2019 21:16:23 -0700 Subject: [PATCH 133/344] 767 --- 767 Reorganize String.py | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 767 Reorganize String.py diff --git a/767 Reorganize String.py b/767 Reorganize String.py new file mode 100644 index 0000000..b608d73 --- /dev/null +++ b/767 Reorganize String.py @@ -0,0 +1,60 @@ +#!/usr/bin/python3 +""" +Given a string S, check if the letters can be rearranged so that two characters +that are adjacent to each other are not the same. + +If possible, output any possible result. If not possible, return the empty +string. + +Example 1: + +Input: S = "aab" +Output: "aba" +Example 2: + +Input: S = "aaab" +Output: "" +Note: + +S will consist of lowercase letters and have length in range [1, 500]. +""" +from collections import defaultdict + + +class Solution: + def reorganizeString(self, S: str) -> str: + """ + piles by max char and circular append + """ + counter = defaultdict(int) + for c in S: + counter[c] += 1 + + lst = [ + (-n, n, c) + for c, n in counter.items() + ] + lst.sort() + piles = [] + _, n, c = lst[0] + for i in range(n): + piles.append([c]) + + cnt = 0 + for _, n, c in lst[1:]: + for _ in range(n): + piles[cnt].append(c) + cnt = (cnt + 1) % len(piles) + + if len(piles) > 1 and len(piles[-2]) == 1: + return "" + + return "".join( + map(lambda x: "".join(x), piles) + ) + + +if __name__ == "__main__": + assert Solution().reorganizeString("vvvlo") == "vlvov" + assert Solution().reorganizeString("aab") == "aba" + assert Solution().reorganizeString("aaab") == "" From 69f34b8441344ea27246469b787c401fa93f87ae Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 10 Mar 2019 21:53:27 -0700 Subject: [PATCH 134/344] 783 --- 783 Minimum Distance Between BST Nodes.py | 56 +++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 783 Minimum Distance Between BST Nodes.py diff --git a/783 Minimum Distance Between BST Nodes.py b/783 Minimum Distance Between BST Nodes.py new file mode 100644 index 0000000..46cddeb --- /dev/null +++ b/783 Minimum Distance Between BST Nodes.py @@ -0,0 +1,56 @@ +#!/usr/bin/python3 +""" +Given a Binary Search Tree (BST) with the root node root, return the minimum +difference between the values of any two different nodes in the tree. + +Example : + +Input: root = [4,2,6,1,3,null,null] +Output: 1 +Explanation: +Note that root is a TreeNode object, not an array. + +The given tree [4,2,6,1,3,null,null] is represented by the following diagram: + + 4 + / \ + 2 6 + / \ + 1 3 + +while the minimum difference in this tree is 1, it occurs between node 1 and +node 2, also between node 3 and node 2. +Note: + +The size of the BST will be between 2 and 100. +The BST is always valid, each node's value is an integer, and each node's value +is different. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.prev = None + self.ret = float('inf') + + def minDiffInBST(self, root: TreeNode) -> int: + """ + in-order traversal + """ + if not root: + return + + self.minDiffInBST(root.left) + if self.prev: + self.ret = min(self.ret, root.val - self.prev) + self.prev = root.val + self.minDiffInBST(root.right) + return self.ret From 387792e51b59609e7b609f62dd88ef84ac1b4734 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 10 Mar 2019 22:12:30 -0700 Subject: [PATCH 135/344] 769 --- 769 Max Chunks To Make Sorted.py | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 769 Max Chunks To Make Sorted.py diff --git a/769 Max Chunks To Make Sorted.py b/769 Max Chunks To Make Sorted.py new file mode 100644 index 0000000..f7dd76b --- /dev/null +++ b/769 Max Chunks To Make Sorted.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" +Given an array arr that is a permutation of [0, 1, ..., arr.length - 1], we +split the array into some number of "chunks" (partitions), and individually sort +each chunk. After concatenating them, the result equals the sorted array. + +What is the most number of chunks we could have made? + +Example 1: + +Input: arr = [4,3,2,1,0] +Output: 1 +Explanation: +Splitting into two or more chunks will not return the required result. +For example, splitting into [4, 3], [2, 1, 0] will result in [3, 4, 0, 1, 2], +which isn't sorted. +Example 2: + +Input: arr = [1,0,2,3,4] +Output: 4 +Explanation: +We can split into two chunks, such as [1, 0], [2, 3, 4]. +However, splitting into [1, 0], [2], [3], [4] is the highest number of chunks +possible. +Note: + +arr will have length in range [1, 10]. +arr[i] will be a permutation of [0, 1, ..., arr.length - 1]. +""" +from typing import List + + +class Solution: + def maxChunksToSorted(self, arr: List[int]) -> int: + """ + compared to the sorted + [0, 1, 2, 3, 4] + [1, 0, 2, 3, 4] + + The largest number in the chunk determines the ending index of the chunk + """ + ret = 0 + cur_idx = 0 + for i in range(len(arr)): + cur_idx = max(cur_idx, arr[i]) + if i == cur_idx: + ret += 1 + + return ret From e23f86ff43496df5a3a8e4d67e0ccbd3559fae4a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 10 Mar 2019 22:27:07 -0700 Subject: [PATCH 136/344] 768 --- 768 Max Chunks To Make Sorted II.py | 61 +++++++++++++++++++++++++++++ 769 Max Chunks To Make Sorted.py | 8 ++-- 2 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 768 Max Chunks To Make Sorted II.py diff --git a/768 Max Chunks To Make Sorted II.py b/768 Max Chunks To Make Sorted II.py new file mode 100644 index 0000000..3e6ac19 --- /dev/null +++ b/768 Max Chunks To Make Sorted II.py @@ -0,0 +1,61 @@ +#!/usr/bin/python3 +""" +This question is the same as "Max Chunks to Make Sorted" except the integers of +the given array are not necessarily distinct, the input array could be up to +length 2000, and the elements could be up to 10**8. + +Given an array arr of integers (not necessarily distinct), we split the array +into some number of "chunks" (partitions), and individually sort each chunk. +After concatenating them, the result equals the sorted array. + +What is the most number of chunks we could have made? + +Example 1: + +Input: arr = [5,4,3,2,1] +Output: 1 +Explanation: +Splitting into two or more chunks will not return the required result. +For example, splitting into [5, 4], [3, 2, 1] will result in [4, 5, 1, 2, 3], +which isn't sorted. +Example 2: + +Input: arr = [2,1,3,4,4] +Output: 4 +Explanation: +We can split into two chunks, such as [2, 1], [3, 4, 4]. +However, splitting into [2, 1], [3], [4], [4] is the highest number of chunks +possible. +Note: + +arr will have length in range [1, 2000]. +arr[i] will be an integer in range [0, 10**8]. +""" +from typing import List +from collections import defaultdict, deque + + +class Solution: + def maxChunksToSorted(self, arr: List[int]) -> int: + """ + not necessarily distinct + sort and assign index + For the smae element, the right ones should get larget assigned index + """ + A = sorted(arr) + hm = defaultdict(deque) + for i, e in enumerate(A): + hm[e].append(i) + + proxy = [] + for e in arr: + proxy.append(hm[e].popleft()) + + ret = 0 + cur_max_idx = 0 + for i, e in enumerate(proxy): + cur_max_idx = max(cur_max_idx, e) + if cur_max_idx == i: + ret += 1 + + return ret diff --git a/769 Max Chunks To Make Sorted.py b/769 Max Chunks To Make Sorted.py index f7dd76b..401cdf2 100644 --- a/769 Max Chunks To Make Sorted.py +++ b/769 Max Chunks To Make Sorted.py @@ -37,13 +37,13 @@ def maxChunksToSorted(self, arr: List[int]) -> int: [0, 1, 2, 3, 4] [1, 0, 2, 3, 4] - The largest number in the chunk determines the ending index of the chunk + The largest number in the chunk determines the ending index of the chunk """ ret = 0 - cur_idx = 0 + cur_max_idx = 0 for i in range(len(arr)): - cur_idx = max(cur_idx, arr[i]) - if i == cur_idx: + cur_max_idx = max(cur_max_idx, arr[i]) + if i == cur_max_idx: ret += 1 return ret From 130da920cf6a4ece9d1dc5c33999ed9ea7b901fe Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 11 Mar 2019 18:20:46 -0700 Subject: [PATCH 137/344] 785 --- 785 Is Graph Bipartite.py | 99 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 785 Is Graph Bipartite.py diff --git a/785 Is Graph Bipartite.py b/785 Is Graph Bipartite.py new file mode 100644 index 0000000..a8f4d64 --- /dev/null +++ b/785 Is Graph Bipartite.py @@ -0,0 +1,99 @@ +#!/usr/bin/python3 +""" +Given an undirected graph, return true if and only if it is bipartite. + +Recall that a graph is bipartite if we can split it's set of nodes into two +independent subsets A and B such that every edge in the graph has one node in A +and another node in B. + +The graph is given in the following form: graph[i] is a list of indexes j for +which the edge between nodes i and j exists. Each node is an integer between 0 +and graph.length - 1. There are no self edges or parallel edges: graph[i] does +not contain i, and it doesn't contain any element twice. + +Example 1: +Input: [[1,3], [0,2], [1,3], [0,2]] +Output: true +Explanation: +The graph looks like this: +0----1 +| | +| | +3----2 +We can divide the vertices into two groups: {0, 2} and {1, 3}. +Example 2: +Input: [[1,2,3], [0,2], [0,1,3], [0,2]] +Output: false +Explanation: +The graph looks like this: +0----1 +| \ | +| \ | +3----2 +We cannot find a way to divide the set of nodes into two independent subsets. + + +Note: + +graph will have length in range [1, 100]. +graph[i] will contain integers in range [0, graph.length - 1]. +graph[i] will not contain i or duplicate values. +The graph is undirected: if any element j is in graph[i], then i will be in +graph[j]. +""" +from collections import defaultdict + + +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + G = graph + color = defaultdict(int) + for k in range(len(G)): + if k not in color: + color[k] = 0 + if not self.dfs(G, k, color): + return False + # if colored, don't vist + + return True + + def dfs(self, G, u, color): + for nbr in G[u]: + if nbr in color: + if color[nbr] == color[u]: + return False + else: + color[nbr] = 1 - color[u] + if not self.dfs(G, nbr, color): + return False + + return True + + +class SolutionError: + def isBipartite(self, graph: List[List[int]]) -> bool: + G = graph + A, B = set(), set() + visited = defaultdict(bool) + for k in range(len(G)): + if not visited[k]: + if not self.dfs(G, visited, k, A, B, True): + return False + + return True + + def dfs(self, G, visited, u, A, B, is_A): + visited[u] = True + if is_A: + A.add(u) + else: + B.add(u) + + for nbr in G[u]: + if nbr in A if is_A else B: + return False + if not visited[nbr]: + if not self.dfs(G, visited, nbr, A, B, False): + return False + + return True From f1a6ae9b1e5ba54d401580e5a0c0654abfed24f6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 11 Mar 2019 18:21:19 -0700 Subject: [PATCH 138/344] 785 --- 785 Is Graph Bipartite.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/785 Is Graph Bipartite.py b/785 Is Graph Bipartite.py index a8f4d64..8f73a2f 100644 --- a/785 Is Graph Bipartite.py +++ b/785 Is Graph Bipartite.py @@ -46,6 +46,10 @@ class Solution: def isBipartite(self, graph: List[List[int]]) -> bool: + """ + coloring the graph + dfs coloring + """ G = graph color = defaultdict(int) for k in range(len(G)): From f734cb93d9bd9818770d4d88676271b70308e1ea Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 11 Mar 2019 20:24:56 -0700 Subject: [PATCH 139/344] 797 --- 785 Is Graph Bipartite.py | 4 +-- 797 All Paths From Source to Target.py | 44 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 797 All Paths From Source to Target.py diff --git a/785 Is Graph Bipartite.py b/785 Is Graph Bipartite.py index 8f73a2f..c440f79 100644 --- a/785 Is Graph Bipartite.py +++ b/785 Is Graph Bipartite.py @@ -48,7 +48,7 @@ class Solution: def isBipartite(self, graph: List[List[int]]) -> bool: """ coloring the graph - dfs coloring + dfs coloring """ G = graph color = defaultdict(int) @@ -67,7 +67,7 @@ def dfs(self, G, u, color): if color[nbr] == color[u]: return False else: - color[nbr] = 1 - color[u] + color[nbr] = 1 - color[u] # can be (0, 1) or (-1, 1) if not self.dfs(G, nbr, color): return False diff --git a/797 All Paths From Source to Target.py b/797 All Paths From Source to Target.py new file mode 100644 index 0000000..409b007 --- /dev/null +++ b/797 All Paths From Source to Target.py @@ -0,0 +1,44 @@ +#!/usr/bin/python3 +""" +Given a directed, acyclic graph of N nodes. Find all possible paths from node 0 +to node N-1, and return them in any order. + +The graph is given as follows: the nodes are 0, 1, ..., graph.length - 1. +graph[i] is a list of all nodes j for which the edge (i, j) exists. + +Example: +Input: [[1,2], [3], [3], []] +Output: [[0,1,3],[0,2,3]] +Explanation: The graph looks like this: +0--->1 +| | +v v +2--->3 +There are two paths: 0 -> 1 -> 3 and 0 -> 2 -> 3. +Note: + +The number of nodes in the graph will be in the range [2, 15]. +You can print different paths in any order, but you should keep the order of +nodes inside one path. +""" + +class Solution: + def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]: + G = graph + ret = [] + visited = [False for _ in G] + self.dfs(G, 0, len(G) - 1, [0], visited, ret) + return ret + + def dfs(self, G, cur, d, cur_path, visited, ret): + if cur == d: + ret.append(list(cur_path)) + + for nbr in G[cur]: + if not visited[nbr]: + visited[nbr] = True + cur_path.append(nbr) + # pre-check + self.dfs(G, nbr, d, cur_path, visited, ret) + cur_path.pop() + visited[nbr] = False From 4ced2040bf694bf4db3c96b28409a2a28543129b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 11 Mar 2019 20:34:02 -0700 Subject: [PATCH 140/344] 796 --- 796 Rotate String.py | 41 ++++++++++++++++++++++++++ 797 All Paths From Source to Target.py | 3 ++ 2 files changed, 44 insertions(+) create mode 100644 796 Rotate String.py diff --git a/796 Rotate String.py b/796 Rotate String.py new file mode 100644 index 0000000..457c04f --- /dev/null +++ b/796 Rotate String.py @@ -0,0 +1,41 @@ +#!/usr/bin/python3 +""" +We are given two strings, A and B. + +A shift on A consists of taking string A and moving the leftmost character to +the rightmost position. For example, if A = 'abcde', then it will be 'bcdea' +after one shift on A. Return True if and only if A can become B after some +number of shifts on A. + +Example 1: +Input: A = 'abcde', B = 'cdeab' +Output: true + +Example 2: +Input: A = 'abcde', B = 'abced' +Output: false +Note: + +A and B will have length at most 100. +""" + + +class Solution: + def rotateString(self, A: str, B: str) -> bool: + """ + brute force O(n^2), shift and compare but short circuit + """ + if len(A) != len(B): + return False + + if not A and not B: + return True + + for i in range(1, len(A)): + for j in range(len(B)): + if A[(i + j) % len(A)] != B[j]: + break + else: + return True + + return False diff --git a/797 All Paths From Source to Target.py b/797 All Paths From Source to Target.py index 409b007..9bd6a8f 100644 --- a/797 All Paths From Source to Target.py +++ b/797 All Paths From Source to Target.py @@ -21,6 +21,8 @@ You can print different paths in any order, but you should keep the order of nodes inside one path. """ +from typing import List + class Solution: def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]: @@ -33,6 +35,7 @@ def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]: def dfs(self, G, cur, d, cur_path, visited, ret): if cur == d: ret.append(list(cur_path)) + return for nbr in G[cur]: if not visited[nbr]: From 3bef131457c455ff70308879c775c9efef9ae9e7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 11 Mar 2019 21:28:25 -0700 Subject: [PATCH 141/344] 795 --- ...umber of Subarrays with Bounded Maximum.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 795 Number of Subarrays with Bounded Maximum.py diff --git a/795 Number of Subarrays with Bounded Maximum.py b/795 Number of Subarrays with Bounded Maximum.py new file mode 100644 index 0000000..fbc42c6 --- /dev/null +++ b/795 Number of Subarrays with Bounded Maximum.py @@ -0,0 +1,72 @@ +#!/usr/bin/python3 +""" +We are given an array A of positive integers, and two positive integers L and R +(L <= R). + +Return the number of (contiguous, non-empty) subarrays such that the value of +the maximum array element in that subarray is at least L and at most R. + +Example : +Input: +A = [2, 1, 4, 3] +L = 2 +R = 3 +Output: 3 +Explanation: There are three subarrays that meet the requirements: [2], [2, 1], +[3]. +Note: + +L, R and A[i] will be an integer in the range [0, 10^9]. +The length of A will be in the range of [1, 50000]. +""" + + +class Solution: + def numSubarrayBoundedMax(self, A: List[int], L: int, R: int) -> int: + """ + DP: Let F[i] be the num subarray with bounded max at A[i] + if L <= A[i] <= R: F[i] = i - prev, where prev is previously invalid F[prev] = 0 + if A[i] > R: F[i] = 0 + if A[i] < L: F[i] = F[i-1] # append itself to every array in F[i-1] + + memory optimization - one counter F is enough + """ + F = 0 + ret = 0 + prev = -1 + for i, a in enumerate(A): + if L <= a <= R: + F = i - prev + ret += F + elif a > R: + F = 0 + prev = i + else: + # F = F + ret += F + + return ret + + def numSubarrayBoundedMax_error(self, A: List[int], L: int, R: int) -> int: + """ + DP: Let F[i] be the num subarray with bounded max at A[i] + if L <= A[i] <= R: F[i] = F[i-1] + 1 # append itself to every array in F[i-1] and one more itself + ^ ERROR + if A[i] > R: F[i] = 0 + if A[i] < L: F[i] = F[i-1] # append itself to every array in F[i-1] + + memory optimization - one counter F is enough + """ + F = 0 + ret = 0 + for a in A: + if L <= a <= R: + F += 1 # error + ret += F + elif a > R: + F = 0 + else: + # F = F + ret += F + + return ret From 52f2e09bbb40f14ebccd62c46f67f28d53d6c264 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 11 Mar 2019 22:04:09 -0700 Subject: [PATCH 142/344] 792 --- 792 Number of Matching Subsequences.py | 69 +++++++++++++++++++ ...umber of Subarrays with Bounded Maximum.py | 1 + 2 files changed, 70 insertions(+) create mode 100644 792 Number of Matching Subsequences.py diff --git a/792 Number of Matching Subsequences.py b/792 Number of Matching Subsequences.py new file mode 100644 index 0000000..9d464d6 --- /dev/null +++ b/792 Number of Matching Subsequences.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +""" +Given string S and a dictionary of words words, find the number of words[i] that +is a subsequence of S. + +Example : +Input: +S = "abcde" +words = ["a", "bb", "acd", "ace"] +Output: 3 +Explanation: There are three words in words that are a subsequence of S: "a", +"acd", "ace". +Note: + +All words in words and S will only consists of lowercase letters. +The length of S will be in the range of [1, 50000]. +The length of words will be in the range of [1, 5000]. +The length of words[i] will be in the range of [1, 50]. +""" +from typing import List +from collections import defaultdict + + +class Solution: + def numMatchingSubseq(self, S: str, words: List[str]) -> int: + """ + Linear O(|S| + sum(|word|)) + no need to if-check + + HashMap + Iterator + """ + itrs_m = defaultdict(list) + for w in words: + itrs_m[w[0]].append( + iter(w[1:]) + ) + for a in S: + itrs = itrs_m.pop(a, []) + for itr in itrs: + v = next(itr, None) + itrs_m[v].append(itr) + + return len(itrs_m[None]) + + def numMatchingSubseq_TLE(self, S: str, words: List[str]) -> int: + """ + Brute force O(|S| |Words| M) + + Is a better way to check subsequence? No + Can we parallel the works? Yes + + go through all words parallel + O(|S| |Words|) + """ + I = [0 for _ in words] + for a in S: + for wi, i in enumerate(I): + if i < len(words[wi]) and words[wi][i] == a: + I[wi] += 1 + + return sum( + 1 + for wi, i in enumerate(I) + if i == len(words[wi]) + ) + + +if __name__ == "__main__": + assert Solution().numMatchingSubseq("abcde", ["a", "bb", "acd", "ace"]) == 3 diff --git a/795 Number of Subarrays with Bounded Maximum.py b/795 Number of Subarrays with Bounded Maximum.py index fbc42c6..76ab6e7 100644 --- a/795 Number of Subarrays with Bounded Maximum.py +++ b/795 Number of Subarrays with Bounded Maximum.py @@ -19,6 +19,7 @@ L, R and A[i] will be an integer in the range [0, 10^9]. The length of A will be in the range of [1, 50000]. """ +from typing import List class Solution: From cfbb86d71862f083323ce33ae92844ac8ade7e79 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 13 Mar 2019 00:03:40 -0700 Subject: [PATCH 143/344] 752 --- 752 Open the Lock.py | 90 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 752 Open the Lock.py diff --git a/752 Open the Lock.py b/752 Open the Lock.py new file mode 100644 index 0000000..a57e65e --- /dev/null +++ b/752 Open the Lock.py @@ -0,0 +1,90 @@ +#!/usr/bin/python3 +""" +You have a lock in front of you with 4 circular wheels. Each wheel has 10 slots: +'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'. The wheels can rotate freely +and wrap around: for example we can turn '9' to be '0', or '0' to be '9'. Each +move consists of turning one wheel one slot. + +The lock initially starts at '0000', a string representing the state of the 4 +wheels. + +You are given a list of deadends dead ends, meaning if the lock displays any of +these codes, the wheels of the lock will stop turning and you will be unable to +open it. + +Given a target representing the value of the wheels that will unlock the lock, +return the minimum total number of turns required to open the lock, or -1 if it +is impossible. + +Example 1: +Input: deadends = ["0201","0101","0102","1212","2002"], target = "0202" +Output: 6 +Explanation: +A sequence of valid moves would be "0000" -> "1000" -> "1100" -> "1200" -> +"1201" -> "1202" -> "0202". +Note that a sequence like "0000" -> "0001" -> "0002" -> "0102" -> "0202" would +be invalid, +because the wheels of the lock become stuck after the display becomes the dead +end "0102". +Example 2: +Input: deadends = ["8888"], target = "0009" +Output: 1 +Explanation: +We can turn the last wheel in reverse to move from "0000" -> "0009". +Example 3: +Input: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], +target = "8888" +Output: -1 +Explanation: +We can't reach the target without getting stuck. +Example 4: +Input: deadends = ["0000"], target = "8888" +Output: -1 +Note: +The length of deadends will be in the range [1, 500]. +target will not be in the list deadends. +Every string in deadends and the string target will be a string of 4 digits from +the 10,000 possibilities '0000' to '9999'. +""" +from typing import List + + +class Solution: + def openLock(self, deadends: List[str], target: str) -> int: + """ + bfs + """ + destination = tuple(int(c) for c in target) + deadends_set = set([ + tuple(int(c) for c in s) + for s in deadends + ]) + q = [(0, 0, 0, 0)] + if q[0] in deadends_set: + return -1 + + step = 0 + visited = set(q) + while q: + cur_q = [] + for e in q: + if e == destination: + return step + for i in range(4): + for delta in (-1, 1): + nxt_lst = list(e) # copy + nxt_lst[i] = (nxt_lst[i] + delta) % 10 # forward or backward + nxt = tuple(nxt_lst) + if nxt not in visited and nxt not in deadends_set: + visited.add(nxt) + cur_q.append(nxt) + + step += 1 + q = cur_q + + return -1 + + +if __name__ == "__main__": + assert Solution().openLock(["8888"], "0009") == 1 + assert Solution().openLock(["8887","8889","8878","8898","8788","8988","7888","9888"], "8888") == -1 From c2e8a49980974dafe48ae5fdf8b7fe6d809acef0 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 13 Mar 2019 21:37:12 -0700 Subject: [PATCH 144/344] 801 --- 752 Open the Lock.py | 4 +- ...imum Swaps To Make Sequences Increasing.py | 83 +++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 801 Minimum Swaps To Make Sequences Increasing.py diff --git a/752 Open the Lock.py b/752 Open the Lock.py index a57e65e..5747071 100644 --- a/752 Open the Lock.py +++ b/752 Open the Lock.py @@ -55,10 +55,10 @@ def openLock(self, deadends: List[str], target: str) -> int: bfs """ destination = tuple(int(c) for c in target) - deadends_set = set([ + deadends_set = set( tuple(int(c) for c in s) for s in deadends - ]) + ) q = [(0, 0, 0, 0)] if q[0] in deadends_set: return -1 diff --git a/801 Minimum Swaps To Make Sequences Increasing.py b/801 Minimum Swaps To Make Sequences Increasing.py new file mode 100644 index 0000000..9a0f0bd --- /dev/null +++ b/801 Minimum Swaps To Make Sequences Increasing.py @@ -0,0 +1,83 @@ +#!/usr/bin/python3 +""" +We have two integer sequences A and B of the same non-zero length. + +We are allowed to swap elements A[i] and B[i]. Note that both elements are in +the same index position in their respective sequences. + +At the end of some number of swaps, A and B are both strictly increasing. +(A sequence is strictly increasing if and only if A[0] < A[1] < A[2] < ... < +A[A.length - 1].) + +Given A and B, return the minimum number of swaps to make both sequences +strictly increasing. It is guaranteed that the given input always makes it +possible. + +Example: +Input: A = [1,3,5,4], B = [1,2,3,7] +Output: 1 +Explanation: +Swap A[3] and B[3]. Then the sequences are: +A = [1, 3, 5, 7] and B = [1, 2, 3, 4] +which are both strictly increasing. +Note: + +A, B are arrays with the same length, and that length will be in the range +[1, 1000]. +A[i], B[i] are integer values in the range [0, 2000]. +""" + + +class Solution: + def minSwap(self, A: List[int], B: List[int]) -> int: + """ + Let F[0][i] be number of swaps to make satisfy if not swap A[i], B[i] + Let F[1][i] be ... if swap A[i], B[i] + + There is a binary array [0, 1, ...] to denote whether to swap A[i], B[i] + without actually swapping the array + """ + n = len(A) + F = [[0 for _ in range(n)] for _ in range(2)] + F[1][0] = 1 + for i in range(1, n): + if A[i] > max(A[i-1], B[i-1]) and B[i] > max(A[i-1], B[i-1]): + # freedom of two options - swap or not swap + F[0][i] = min(F[0][i-1], F[1][i-1]) + F[1][i] = min(F[0][i-1], F[1][i-1]) + 1 + elif A[i] > A[i-1] and B[i] > B[i-1]: + # elif meaning that has to stick with previous swap choice + # A[i] <= B[i-1] and B[i] <=A[i-1], cannot flip + F[0][i] = F[0][i-1] + F[1][i] = F[1][i-1] + 1 + else: + # has to swap, flip + F[0][i] = F[1][i-1] + F[1][i] = F[0][i-1] + 1 + + return min(F[0][n-1], F[1][n-1]) + + def minSwap_error(self, A: List[int], B: List[int]) -> int: + """ + for length 2 + swap A[0] and B[0] is the same as swapping A[1], B[2] + for length 3 + it is different + 1 10 19 + 3 2 8 + swap can be length - times (swap the other) + """ + t = 0 + for i in range(1, len(A)): + if A[i] <= A[i-1] or B[i] <= B[i-1]: + t += 1 + if t < i + 1 - t: + A[i], B[i] = B[i], A[i] + else: + t = i + 1 - t + + return t + + +if __name__ == "__main__": + assert Solution().minSwap([0,4,4,5,9], [0,1,6,8,10]) From f4675ce2de93463fd1da6b79bcac1fe1684eac7e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 16 Mar 2019 14:28:15 -0700 Subject: [PATCH 145/344] 802 --- 802 Find Eventual Safe States.py | 75 ++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 802 Find Eventual Safe States.py diff --git a/802 Find Eventual Safe States.py b/802 Find Eventual Safe States.py new file mode 100644 index 0000000..8356933 --- /dev/null +++ b/802 Find Eventual Safe States.py @@ -0,0 +1,75 @@ +#!/usr/bin/python3 +""" +In a directed graph, we start at some node and every turn, walk along a directed +edge of the graph. If we reach a node that is terminal (that is, it has no +outgoing directed edges), we stop. + +Now, say our starting node is eventually safe if and only if we must eventually +walk to a terminal node. More specifically, there exists a natural number K so +that for any choice of where to walk, we must have stopped at a terminal node in +less than K steps. + +Which nodes are eventually safe? Return them as an array in sorted order. + +The directed graph has N nodes with labels 0, 1, ..., N-1, where N is the length +of graph. The graph is given in the following form: graph[i] is a list of +labels j such that (i, j) is a directed edge of the graph. + +Example: +Input: graph = [[1,2],[2,3],[5],[0],[5],[],[]] +Output: [2,4,5,6] +Here is a diagram of the above graph. + +Illustration of graph + +Note: + +graph will have length at most 10000. +The number of edges in the graph will not exceed 32000. +Each graph[i] will be a sorted list of different integers, chosen within the +range [0, graph.length - 1]. +""" +from typing import List, Set + + +class Solution: + def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]: + """ + detect cycle in the node + prune by nodes with no cycle + """ + visit: List[int] = [0 for _ in graph] # 0 not visted, 1 processing, 2 visited + acyclic: Set[int] = set() + for u in range(len(graph)): + if visit[u] == 0: + self.dfs(graph, u, visit, acyclic) + + return [ + u + for u in range(len(graph)) + if u in acyclic + ] + + def dfs(self, graph, cur, visit, acyclic): + visit[cur] = 1 + for nbr in graph[cur]: + if visit[nbr] == 2: + if nbr in acyclic: + continue + else: + break + if visit[nbr] == 1: + break + if visit[nbr] == 0 and not self.dfs(graph, nbr, visit, acyclic): + break + else: + acyclic.add(cur) + visit[cur] = 2 + return True + + visit[cur] = 2 + return False + + +if __name__ == "__main__": + assert Solution().eventualSafeNodes([[1,2],[2,3],[5],[0],[5],[],[]]) == [2,4,5,6] From 8b778f3a15debac12ef9001102865ca2f4a2dc61 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 16 Mar 2019 14:49:19 -0700 Subject: [PATCH 146/344] 814 --- ... => 787 Cheapest Flights Within K Stops.py | 0 814 Binary Tree Pruning.py | 65 +++++++++++++++++++ 2 files changed, 65 insertions(+) rename 787 Cheapest Flights Within K Stops py3.py => 787 Cheapest Flights Within K Stops.py (100%) create mode 100644 814 Binary Tree Pruning.py diff --git a/787 Cheapest Flights Within K Stops py3.py b/787 Cheapest Flights Within K Stops.py similarity index 100% rename from 787 Cheapest Flights Within K Stops py3.py rename to 787 Cheapest Flights Within K Stops.py diff --git a/814 Binary Tree Pruning.py b/814 Binary Tree Pruning.py new file mode 100644 index 0000000..7a969e1 --- /dev/null +++ b/814 Binary Tree Pruning.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 +""" +We are given the head node root of a binary tree, where additionally every +node's value is either a 0 or a 1. + +Return the same tree where every subtree (of the given tree) not containing a 1 +has been removed. + +(Recall that the subtree of a node X is X, plus every node that is a descendant +of X.) + +Example 1: +Input: [1,null,0,0,1] +Output: [1,null,0,null,1] + +Explanation: +Only the red nodes satisfy the property "every subtree not containing a 1". +The diagram on the right represents the answer. + + +Example 2: +Input: [1,0,1,0,0,0,1] +Output: [1,null,1,null,1] + + + +Example 3: +Input: [1,1,0,1,1,0,1,0] +Output: [1,1,0,1,1,null,1] + + + +Note: + +The binary tree will have at most 100 nodes. +The value of each node will only be 0 or 1. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +from typing import Tuple + + +class Solution: + def pruneTree(self, root: TreeNode) -> TreeNode: + root, _ = self.prune(root) + return root + + def prune(self, node) -> Tuple[TreeNode, bool]: + if not node: + return None, False + + node.left, contain_left = self.prune(node.left) + node.right, contain_right = self.prune(node.right) + if not contain_left and not contain_right and node.val == 0: + return None, False + + return node, True From f867db87681713ae9b6bd48c8d54bd28597f3f8b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 16 Mar 2019 15:35:44 -0700 Subject: [PATCH 147/344] 813 --- 813 Largest Sum of Averages.py | 99 ++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 813 Largest Sum of Averages.py diff --git a/813 Largest Sum of Averages.py b/813 Largest Sum of Averages.py new file mode 100644 index 0000000..4d01080 --- /dev/null +++ b/813 Largest Sum of Averages.py @@ -0,0 +1,99 @@ +#!/usr/bin/python3 +""" +We partition a row of numbers A into at most K adjacent (non-empty) groups, then +our score is the sum of the average of each group. What is the largest score we +can achieve? + +Note that our partition must use every number in A, and that scores are not +necessarily integers. + +Example: +Input: +A = [9,1,2,3,9] +K = 3 +Output: 20 +Explanation: +The best choice is to partition A into [9], [1, 2, 3], [9]. The answer is +9 + (1 + 2 + 3) / 3 + 9 = 20. +We could have also partitioned A into [9, 1], [2], [3, 9], for example. +That partition would lead to a score of 5 + 2 + 6 = 13, which is worse. + + +Note: + +1 <= A.length <= 100. +1 <= A[i] <= 10000. +1 <= K <= A.length. +Answers within 10^-6 of the correct answer will be accepted as correct. +""" +from typing import List + + +class Solution: + def largestSumOfAverages(self, A: List[int], K: int) -> float: + """ + Memoized Backtracking + Prefix sum + My first hunch is correct + Complexity O(N^2 * K), mark sum and different way of forming groups + (inserting dividers) + + calculating each F[l, k] will need O(N) time, thus total O(n^2 k) + """ + n = len(A) + prefix_sum = [0 for _ in range(n+1)] + for i in range(1, n+1): + prefix_sum[i] = prefix_sum[i-1] + A[i-1] + + F = {} + self.dfs(A, n, prefix_sum, F, K) + return F[n, K] + + def dfs(self, A, l, prefix_sum, F, k): + """ + dfs search divide + make A[:l] k groups + """ + if l < k: + return -float('inf') + + if (l, k) not in F: + if k == 1: + ret = prefix_sum[l] / l + else: + n = len(A) + ret = -float('inf') + for j in range(l-1, -1, -1): + trail = (prefix_sum[l] - prefix_sum[j]) / (l - j) + ret = max( + ret, + self.dfs(A, j, prefix_sum, F, k-1) + trail + ) + + F[l, k] = ret + + return F[l, k] + + def dfs_error(self, A, i, prefix_sum, F, k): + """ + inconvenient + + dfs search divide + make A[:i] 1 group + make A[i:] k - 1 group + """ + if (i, k) not in F: + ret = 0 + avg = prefix_sum[i] / i + ret += avg + ret += max( + # error + self.dfs(A, j, prefix_sum, F, k - 1) + for j in range(i, len(A)) + ) + F[i, k] = ret + + return F[i, k] + + +if __name__ == "__main__": + assert Solution().largestSumOfAverages([9,1,2,3,9], 3) == 20 From e314417d5d9e1b675369f97fd10a53b6ffb04992 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 16 Mar 2019 18:28:50 -0700 Subject: [PATCH 148/344] 820 --- 820 Short Encoding of Words.py | 57 ++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 820 Short Encoding of Words.py diff --git a/820 Short Encoding of Words.py b/820 Short Encoding of Words.py new file mode 100644 index 0000000..2dd5e46 --- /dev/null +++ b/820 Short Encoding of Words.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +""" +Given a list of words, we may encode it by writing a reference string S and a +list of indexes A. + +For example, if the list of words is ["time", "me", "bell"], we can write it as +S = "time#bell#" and indexes = [0, 2, 5]. + +Then for each index, we will recover the word by reading from the reference +string from that index until we reach a "#" character. + +What is the length of the shortest reference string S possible that encodes the +given words? + +Example: + +Input: words = ["time", "me", "bell"] +Output: 10 +Explanation: S = "time#bell#" and indexes = [0, 2, 5]. + +Note: + +1 <= words.length <= 2000. +1 <= words[i].length <= 7. +Each word has only lowercase letters. +""" +from typing import List + + +class Solution: + def minimumLengthEncoding(self, words: List[str]) -> int: + """ + suffix trie + only suffix matters + + fast trie with dict + """ + root = {} + leaves = [] + for word in set(words): + cur = root + for c in word[::-1]: + nxt = cur.get(c, {}) + cur[c] = nxt + cur = nxt + + leaves.append((cur, len(word))) + + return sum( + l + 1 + for node, l in leaves + if len(node) == 0 # no child + ) + + +if __name__ == "__main__": + assert Solution().minimumLengthEncoding(["time", "me", "bell"]) == 10 From d2c3389848ee5c8218753af836df9673860610ba Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 16 Mar 2019 19:32:06 -0700 Subject: [PATCH 149/344] 830 --- 820 Short Encoding of Words.py | 6 ++-- 830 Positions of Large Groups.py | 50 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 830 Positions of Large Groups.py diff --git a/820 Short Encoding of Words.py b/820 Short Encoding of Words.py index 2dd5e46..3f9affa 100644 --- a/820 Short Encoding of Words.py +++ b/820 Short Encoding of Words.py @@ -36,7 +36,7 @@ def minimumLengthEncoding(self, words: List[str]) -> int: fast trie with dict """ root = {} - leaves = [] + ends = [] for word in set(words): cur = root for c in word[::-1]: @@ -44,11 +44,11 @@ def minimumLengthEncoding(self, words: List[str]) -> int: cur[c] = nxt cur = nxt - leaves.append((cur, len(word))) + ends.append((cur, len(word))) return sum( l + 1 - for node, l in leaves + for node, l in ends if len(node) == 0 # no child ) diff --git a/830 Positions of Large Groups.py b/830 Positions of Large Groups.py new file mode 100644 index 0000000..ee987bf --- /dev/null +++ b/830 Positions of Large Groups.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +""" +In a string S of lowercase letters, these letters form consecutive groups of the +same character. + +For example, a string like S = "abbxxxxzyy" has the groups "a", "bb", "xxxx", +"z" and "yy". + +Call a group large if it has 3 or more characters. We would like the starting +and ending positions of every large group. + +The final answer should be in lexicographic order. + + + +Example 1: + +Input: "abbxxxxzzy" +Output: [[3,6]] +Explanation: "xxxx" is the single large group with starting 3 and ending +positions 6. +Example 2: + +Input: "abc" +Output: [] +Explanation: We have "a","b" and "c" but no large group. +Example 3: + +Input: "abcdddeeeeaabbbcd" +Output: [[3,5],[6,9],[12,14]] + +Note: 1 <= S.length <= 1000 +""" +from typing import List + + +class Solution: + def largeGroupPositions(self, S: str) -> List[List[int]]: + i = 0 + j = 0 + ret = [] + n = len(S) + while j < n: + while j < n and S[i] == S[j]: + j += 1 + if j - i >= 3: + ret.append([i, j - 1]) + i = j + + return ret From 94cf49c184f99302bfdf562ee09f8e540e16ee62 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 16 Mar 2019 20:13:31 -0700 Subject: [PATCH 150/344] 823 --- 823 Binary Trees With Factors.py | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 823 Binary Trees With Factors.py diff --git a/823 Binary Trees With Factors.py b/823 Binary Trees With Factors.py new file mode 100644 index 0000000..efdad53 --- /dev/null +++ b/823 Binary Trees With Factors.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +""" +Given an array of unique integers, each integer is strictly greater than 1. + +We make a binary tree using these integers and each number may be used for any +number of times. + +Each non-leaf node's value should be equal to the product of the values of it's +children. + +How many binary trees can we make? Return the answer modulo 10 ** 9 + 7. + +Example 1: + +Input: A = [2, 4] +Output: 3 +Explanation: We can make these trees: [2], [4], [4, 2, 2] +Example 2: + +Input: A = [2, 4, 5, 10] +Output: 7 +Explanation: We can make these trees: [2], [4], [5], [10], [4, 2, 2], +[10, 2, 5], [10, 5, 2]. + +Note: + +1 <= A.length <= 1000. +2 <= A[i] <= 10 ^ 9. +""" +from typing import List + + +MOD = 10 ** 9 + 7 + + +class Solution: + def numFactoredBinaryTrees(self, A: List[int]) -> int: + """ + Let F[i] be the number of factored binary tree rooted at i + """ + A.sort() + F = {} + for i in range(len(A)): + F[A[i]] = 1 + for j in range(i): + if A[i] % A[j] == 0 and A[i] // A[j] in F: + F[A[i]] += F[A[j]] * F[A[i] // A[j]] # #left * #right + F[A[i]] %= MOD + + return sum(F.values()) % MOD From f3d037cca4ea935a7351dd80c6e6da7f10c3dc3c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Mar 2019 13:50:06 -0700 Subject: [PATCH 151/344] 832 --- 832 Flipping an Image.py | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 832 Flipping an Image.py diff --git a/832 Flipping an Image.py b/832 Flipping an Image.py new file mode 100644 index 0000000..c016d85 --- /dev/null +++ b/832 Flipping an Image.py @@ -0,0 +1,42 @@ +#!/usr/bin/python3 +""" +Given a binary matrix A, we want to flip the image horizontally, then invert it +, and return the resulting image. + +To flip an image horizontally means that each row of the image is reversed. +For example, flipping [1, 1, 0] horizontally results in [0, 1, 1]. + +To invert an image means that each 0 is replaced by 1, and each 1 is replaced by +0. For example, inverting [0, 1, 1] results in [1, 0, 0]. + +Example 1: + +Input: [[1,1,0],[1,0,1],[0,0,0]] +Output: [[1,0,0],[0,1,0],[1,1,1]] +Explanation: First reverse each row: [[0,1,1],[1,0,1],[0,0,0]]. +Then, invert the image: [[1,0,0],[0,1,0],[1,1,1]] +Example 2: + +Input: [[1,1,0,0],[1,0,0,1],[0,1,1,1],[1,0,1,0]] +Output: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]] +Explanation: First reverse each row: [[0,0,1,1],[1,0,0,1],[1,1,1,0],[0,1,0,1]]. +Then invert the image: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]] +Notes: + +1 <= A.length = A[0].length <= 20 +0 <= A[i][j] <= 1 +""" +from typing import List + + +class Solution: + def flipAndInvertImage(self, A: List[List[int]]) -> List[List[int]]: + """ + one pass + """ + for row in A: + prev = list(row) + for i in range(len(row)): + row[i] = prev[-1-i] ^ 1 + + return A From b887aa140f8c74d7e7cf16ba0c135bcb27acfde9 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Mar 2019 14:32:17 -0700 Subject: [PATCH 152/344] 836 --- 836 Rectangle Overlap.py | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 836 Rectangle Overlap.py diff --git a/836 Rectangle Overlap.py b/836 Rectangle Overlap.py new file mode 100644 index 0000000..f7f2011 --- /dev/null +++ b/836 Rectangle Overlap.py @@ -0,0 +1,55 @@ +#!/usr/bin/python3 +""" +A rectangle is represented as a list [x1, y1, x2, y2], where (x1, y1) are the +coordinates of its bottom-left corner, and (x2, y2) are the coordinates of its +top-right corner. + +Two rectangles overlap if the area of their intersection is positive. To be +clear, two rectangles that only touch at the corner or edges do not overlap. + +Given two (axis-aligned) rectangles, return whether they overlap. + +Example 1: + +Input: rec1 = [0,0,2,2], rec2 = [1,1,3,3] +Output: true +Example 2: + +Input: rec1 = [0,0,1,1], rec2 = [1,0,2,1] +Output: false +Notes: + +Both rectangles rec1 and rec2 are lists of 4 integers. +All coordinates in rectangles will be between -10^9 and 10^9. +""" +from typing import List + + +class Solution: + def isRectangleOverlap(self, rec1: List[int], rec2: List[int]) -> bool: + """ + De Morgan's Law + 0 1 2 3 + [left_x, left_y, right_x, right_y] + + Non-overlap if on the left, right, top, bottom + """ + return not ( + rec1[2] <= rec2[0] or # left + rec1[0] >= rec2[2] or # right + rec1[1] >= rec2[3] or # top + rec1[3] <= rec2[1] # bottom + ) + + + def isRectangleOverlap_error(self, rec1: List[int], rec2: List[int]) -> bool: + if rec1[0] > rec2[0]: + return self.isRectangleOverlap(rec2, rec1) + + return ( + rect1[0] < rect2[0] < rec1[2] and + ( + rec2[1] < rect1[3] < rect2[3] or + rec2[3] < rect1[3] < rect2[1] + ) + ) From 1bb8ff9e8d95a0b750c2d1c8f84421d620b57461 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Mar 2019 16:54:30 -0700 Subject: [PATCH 153/344] 807 --- 807 Max Increase to Keep City Skyline.py | 71 ++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 807 Max Increase to Keep City Skyline.py diff --git a/807 Max Increase to Keep City Skyline.py b/807 Max Increase to Keep City Skyline.py new file mode 100644 index 0000000..ab6c9b0 --- /dev/null +++ b/807 Max Increase to Keep City Skyline.py @@ -0,0 +1,71 @@ +#!/usr/bin/python3 +""" +In a 2 dimensional array grid, each value grid[i][j] represents the height of a +building located there. We are allowed to increase the height of any number of +buildings, by any amount (the amounts can be different for different buildings). +Height 0 is considered to be a building as well. + +At the end, the "skyline" when viewed from all four directions of the grid, i.e. +top, bottom, left, and right, must be the same as the skyline of the original +grid. A city's skyline is the outer contour of the rectangles formed by all the +buildings when viewed from a distance. See the following example. + +What is the maximum total sum that the height of the buildings can be increased? + +Example: +Input: grid = [[3,0,8,4],[2,4,5,7],[9,2,6,3],[0,3,1,0]] +Output: 35 +Explanation: +The grid is: +[ [3, 0, 8, 4], + [2, 4, 5, 7], + [9, 2, 6, 3], + [0, 3, 1, 0] ] + +The skyline viewed from top or bottom is: [9, 4, 8, 7] +The skyline viewed from left or right is: [8, 7, 9, 3] + +The grid after increasing the height of buildings without affecting skylines is: + +gridNew = [ [8, 4, 8, 7], + [7, 4, 7, 7], + [9, 4, 8, 7], + [3, 3, 3, 3] ] + +Notes: + +1 < grid.length = grid[0].length <= 50. +All heights grid[i][j] are in the range [0, 100]. +All buildings in grid[i][j] occupy the entire grid cell: that is, they are a +1 x 1 x grid[i][j] rectangular prism. +""" +from typing import List + + +class Solution: + def maxIncreaseKeepingSkyline(self, grid: List[List[int]]) -> int: + """ + grow the to limit constraint by 2D skyline + """ + m, n = len(grid), len(grid[0]) + # left to right projection + lr = [ + max(row) + for row in grid + ] + # top to bottom projection + tb = [ + max( + grid[i][j] + for i in range(m) + ) + for j in range(n) + ] + + ret = 0 + for i in range(m): + for j in range(n): + diff = min(lr[i], tb[j]) - grid[i][j] + ret += diff + + return ret From f679aa8e4e0a1cdbb96078779d825de3eab5a3fc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Mar 2019 20:54:49 -0700 Subject: [PATCH 154/344] 841 --- 841 Keys and Rooms.py | 60 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 841 Keys and Rooms.py diff --git a/841 Keys and Rooms.py b/841 Keys and Rooms.py new file mode 100644 index 0000000..5f82f69 --- /dev/null +++ b/841 Keys and Rooms.py @@ -0,0 +1,60 @@ +#!/usr/bin/python3 +""" +There are N rooms and you start in room 0. Each room has a distinct number in +0, 1, 2, ..., N-1, and each room may have some keys to access the next room. + +Formally, each room i has a list of keys rooms[i], and each key rooms[i][j] is +an integer in [0, 1, ..., N-1] where N = rooms.length. A key rooms[i][j] = v +opens the room with number v. + +Initially, all the rooms start locked (except for room 0). + +You can walk back and forth between rooms freely. + +Return true if and only if you can enter every room. + +Example 1: + +Input: [[1],[2],[3],[]] +Output: true +Explanation: +We start in room 0, and pick up key 1. +We then go to room 1, and pick up key 2. +We then go to room 2, and pick up key 3. +We then go to room 3. Since we were able to go to every room, we return true. +Example 2: + +Input: [[1,3],[3,0,1],[2],[0]] +Output: false +Explanation: We can't enter the room with number 2. +Note: + +1 <= rooms.length <= 1000 +0 <= rooms[i].length <= 1000 +The number of keys in all rooms combined is at most 3000. +""" +from typing import List + + +class Solution: + def canVisitAllRooms(self, G: List[List[int]]) -> bool: + """ + starting from 0 + + need a queue to keep track of processing nodes? Implicitly handle by dfs + stacks + """ + n = len(G) + visited = [0 for _ in range(n)] # 0 locked, 1 visited + self.dfs(G, 0, visited) + return all(e == 1 for e in visited) + + def dfs(self, G, u, visited): + visited[u] = 1 + for nbr in G[u]: + if not visited[nbr]: + self.dfs(G, nbr, visited) + +if __name__ == "__main__": + assert Solution().canVisitAllRooms([[1],[2],[3],[]]) == True + assert Solution().canVisitAllRooms([[1,3],[3,0,1],[2],[0]]) == False From fec4c5e430269846c5a2331dc4a7387c339b5837 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Mar 2019 21:33:33 -0700 Subject: [PATCH 155/344] 842 --- 841 Keys and Rooms.py | 1 + 842 Split Array into Fibonacci Sequence.py | 103 +++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 842 Split Array into Fibonacci Sequence.py diff --git a/841 Keys and Rooms.py b/841 Keys and Rooms.py index 5f82f69..bf91493 100644 --- a/841 Keys and Rooms.py +++ b/841 Keys and Rooms.py @@ -55,6 +55,7 @@ def dfs(self, G, u, visited): if not visited[nbr]: self.dfs(G, nbr, visited) + if __name__ == "__main__": assert Solution().canVisitAllRooms([[1],[2],[3],[]]) == True assert Solution().canVisitAllRooms([[1,3],[3,0,1],[2],[0]]) == False diff --git a/842 Split Array into Fibonacci Sequence.py b/842 Split Array into Fibonacci Sequence.py new file mode 100644 index 0000000..5d93d4f --- /dev/null +++ b/842 Split Array into Fibonacci Sequence.py @@ -0,0 +1,103 @@ +#!/usr/bin/python3 +""" +Given a string S of digits, such as S = "123456579", we can split it into a +Fibonacci-like sequence [123, 456, 579]. + +Formally, a Fibonacci-like sequence is a list F of non-negative integers such +that: + +0 <= F[i] <= 2^31 - 1, (that is, each integer fits a 32-bit signed integer +type); +F.length >= 3; +and F[i] + F[i+1] = F[i+2] for all 0 <= i < F.length - 2. +Also, note that when splitting the string into pieces, each piece must not have +extra leading zeroes, except if the piece is the number 0 itself. + +Return any Fibonacci-like sequence split from S, or return [] if it cannot be +done. + +Example 1: + +Input: "123456579" +Output: [123,456,579] +Example 2: + +Input: "11235813" +Output: [1,1,2,3,5,8,13] +Example 3: + +Input: "112358130" +Output: [] +Explanation: The task is impossible. +Example 4: + +Input: "0123" +Output: [] +Explanation: Leading zeroes are not allowed, so "01", "2", "3" is not valid. +Example 5: + +Input: "1101111" +Output: [110, 1, 111] +Explanation: The output [11, 0, 11, 11] would also be accepted. +Note: + +1 <= S.length <= 200 +S contains only digits. +""" +from typing import List + + +MAX = 2 ** 31 - 1 + + +class Solution: + def splitIntoFibonacci(self, S: str) -> List[int]: + """ + The first two elements of the array uniquely determine the rest of the + sequence. + + 2^31 - 1 is length 10 + brute force + """ + l = len(S) + for i in range(1, l + 1): + num_str = S[:i] + if len(num_str) > 1 and num_str.startswith("0"): + continue + + num = int(num_str) + if num > MAX: + break + + for j in range(i + 1, l + 1): + num2_str = S[i:j] + if len(num2_str) > 1 and num2_str.startswith("0"): + continue + + num2 = int(num2_str) + if num2 > MAX: + break + + ret = [num, num2] + k = j + while k < l: + nxt = ret[-1] + ret[-2] + if nxt > MAX: + break + + nxt_str = str(nxt) + if S[k:k+len(nxt_str)] == nxt_str: + k = k + len(nxt_str) + ret.append(nxt) + else: + break + else: + if k == l and len(ret) >= 3: + return ret + + return [] + + +if __name__ == "__main__": + assert Solution().splitIntoFibonacci("123456579") == [123,456,579] + assert Solution().splitIntoFibonacci("01123581321345589") == [0,1,1,2,3,5,8,13,21,34,55,89] From f1189d182217baca432fbca4107f666236b9bf22 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 17 Mar 2019 22:22:16 -0700 Subject: [PATCH 156/344] 844 --- 844 Backspace String Compare.py | 58 +++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 844 Backspace String Compare.py diff --git a/844 Backspace String Compare.py b/844 Backspace String Compare.py new file mode 100644 index 0000000..9be425a --- /dev/null +++ b/844 Backspace String Compare.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 +""" +Given two strings S and T, return if they are equal when both are typed into +empty text editors. # means a backspace character. + +Example 1: + +Input: S = "ab#c", T = "ad#c" +Output: true +Explanation: Both S and T become "ac". +Example 2: + +Input: S = "ab##", T = "c#d#" +Output: true +Explanation: Both S and T become "". +Example 3: + +Input: S = "a##c", T = "#a#c" +Output: true +Explanation: Both S and T become "c". +Example 4: + +Input: S = "a#c", T = "b" +Output: false +Explanation: S becomes "c" while T becomes "b". +Note: + +1 <= S.length <= 200 +1 <= T.length <= 200 +S and T only contain lowercase letters and '#' characters. +Follow up: + +Can you solve it in O(N) time and O(1) space? +""" + + +class Solution: + def backspaceCompare(self, S: str, T: str) -> bool: + """ + stk + use a stk to build the string + + Another approach: + Iterate the string reversely. When encountering "#", count, and skip + the chars based on skip count. + """ + return self.make_stk(S) == self.make_stk(T) + + def make_stk(self, S): + stk = [] + for s in S: + if s == "#": + if stk: + stk.pop() + else: + stk.append(s) + + return stk From c8841b49ef9e66fb32ee562b582d580a2509f0a2 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 18 Mar 2019 21:33:47 -0700 Subject: [PATCH 157/344] 845 --- 845 Longest Mountain in Array.py | 114 +++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 845 Longest Mountain in Array.py diff --git a/845 Longest Mountain in Array.py b/845 Longest Mountain in Array.py new file mode 100644 index 0000000..e8de2c3 --- /dev/null +++ b/845 Longest Mountain in Array.py @@ -0,0 +1,114 @@ +#!/usr/bin/python3 +""" +Let's call any (contiguous) subarray B (of A) a mountain if the following +properties hold: + +B.length >= 3 +There exists some 0 < i < B.length - 1 such that B[0] < B[1] < ... B[i-1] < +B[i] > B[i+1] > ... > B[B.length - 1] +(Note that B could be any subarray of A, including the entire array A.) + +Given an array A of integers, return the length of the longest mountain. + +Return 0 if there is no mountain. + +Example 1: + +Input: [2,1,4,7,3,2,5] +Output: 5 +Explanation: The largest mountain is [1,4,7,3,2] which has length 5. +Example 2: + +Input: [2,2,2] +Output: 0 +Explanation: There is no mountain. +Note: + +0 <= A.length <= 10000 +0 <= A[i] <= 10000 +Follow up: + +Can you solve it using only one pass? +Can you solve it in O(1) space? +""" +from typing import List + + +class Solution: + def longestMountain(self, A: List[int]) -> int: + """ + dp + """ + ret = 0 + up_cnt = 0 + down_cnt = 0 + for i in range(1, len(A)): + if down_cnt and A[i] >= A[i-1]: + up_cnt = 0 + down_cnt = 0 + if A[i] > A[i-1]: + up_cnt += 1 + elif A[i] < A[i-1]: + down_cnt += 1 + if up_cnt and down_cnt: + ret = max(ret, up_cnt + down_cnt + 1) + + return ret + + def longestMountain(self, A: List[int]) -> int: + """ + dp + """ + n = len(A) + U = [0 for _ in A] # up counter from left to right + D = [0 for _ in A] # down counter from right to left + for i in range(1, n): + if A[i] > A[i-1]: + U[i] = U[i-1] + 1 + for i in range(n-2, -1, -1): + if A[i] > A[i+1]: + D[i] = D[i+1] + 1 + + ret = 0 + for i in range(n): + if U[i] > 0 and D[i] > 0: + ret = max(ret, U[i] + D[i] + 1) + + return ret + + def longestMountain_complicated(self, A: List[int]) -> int: + """ + a flag to indicate expecting increase or decrease + one-pass can + """ + ret = 0 + l = 1 + expect_incr = True + for i in range(1, len(A)): + if expect_incr: + if A[i] > A[i-1]: + l += 1 + elif A[i] < A[i-1] and l >= 2: + expect_incr = False + l += 1 + ret = max(ret, l) + else: + l = 1 + + else: + if A[i] < A[i-1]: + l += 1 + ret = max(ret, l) + elif A[i] == A[i-1]: + expect_incr = True + l = 1 + else: + expect_incr = True + l = 2 + + return ret if ret >= 3 else 0 + + +if __name__ == "__main__": + assert Solution().longestMountain([2,1,4,7,3,2,5]) == 5 + assert Solution().longestMountain([9,8,7,6,5,4,3,2,1,0]) == 0 From faac279566d689976f2a031ecf89aebb0d54c8a3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 18 Mar 2019 23:51:36 -0700 Subject: [PATCH 158/344] 846 --- 846 Hand of Straights.py | 95 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 846 Hand of Straights.py diff --git a/846 Hand of Straights.py b/846 Hand of Straights.py new file mode 100644 index 0000000..4e538a9 --- /dev/null +++ b/846 Hand of Straights.py @@ -0,0 +1,95 @@ +#!/usr/bin/python3 +""" +Alice has a hand of cards, given as an array of integers. + +Now she wants to rearrange the cards into groups so that each group is size W, +and consists of W consecutive cards. + +Return true if and only if she can. + + + +Example 1: + +Input: hand = [1,2,3,6,2,3,4,7,8], W = 3 +Output: true +Explanation: Alice's hand can be rearranged as [1,2,3],[2,3,4],[6,7,8]. +Example 2: + +Input: hand = [1,2,3,4,5], W = 4 +Output: false +Explanation: Alice's hand can't be rearranged into groups of 4. + + +Note: + +1 <= hand.length <= 10000 +0 <= hand[i] <= 10^9 +1 <= W <= hand.length +""" +from typing import List +from collections import Counter, deque +import heapq + + +class Solution: + def isNStraightHand(self, A: List[int], W: int) -> bool: + """ + sort + queue + + prev = previous value + prev_cnt = previous value count + """ + q = deque() + counter = Counter(A) + prev = 0 + prev_cnt = 0 + for k in sorted(counter): # sorted by key + if prev_cnt > counter[k] or prev_cnt > 0 and k > prev + 1: + return False + + q.append(counter[k] - prev_cnt) + prev, prev_cnt = k, counter[k] + if len(q) == W: + c = q.popleft() + prev_cnt -= c + + return prev_cnt == 0 + + def isNStraightHand_heap(self, A: List[int], W: int) -> bool: + """ + sort + heap + O(N log N + N log N) + """ + A.sort() + if len(A) % W != 0: + return False + if W == 1: + return True + + + h = [] # tuple of (-3, [1, 2, 3]) + for a in A: + if not h: + h = [(a, [a])] + continue + + if a == h[0][1][-1]: + heapq.heappush(h, (a, [a])) + elif a == h[0][1][-1] + 1: + _, lst = heapq.heappop(h) + lst.append(a) + if len(lst) < W: + heapq.heappush(h, (a, lst)) + else: + return False + + if h: + return False + + return True + + +if __name__ == "__main__": + assert Solution().isNStraightHand([1,2,3,6,2,3,4,7,8], 3) == True + assert Solution().isNStraightHand([1,1,2,2,3,3], 3) == True From db45bb43e63bcc0e235d84b170dbe584c27f85b3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 19 Mar 2019 22:47:06 -0700 Subject: [PATCH 159/344] 848 --- 848 Shifting Letters.py | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 848 Shifting Letters.py diff --git a/848 Shifting Letters.py b/848 Shifting Letters.py new file mode 100644 index 0000000..c489459 --- /dev/null +++ b/848 Shifting Letters.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 +""" +We have a string S of lowercase letters, and an integer array shifts. + +Call the shift of a letter, the next letter in the alphabet, (wrapping around so +that 'z' becomes 'a'). + +For example, shift('a') = 'b', shift('t') = 'u', and shift('z') = 'a'. + +Now for each shifts[i] = x, we want to shift the first i+1 letters of S, x times. + +Return the final string after all such shifts to S are applied. + +Example 1: + +Input: S = "abc", shifts = [3,5,9] +Output: "rpl" +Explanation: +We start with "abc". +After shifting the first 1 letters of S by 3, we have "dbc". +After shifting the first 2 letters of S by 5, we have "igc". +After shifting the first 3 letters of S by 9, we have "rpl", the answer. +Note: + +1 <= S.length = shifts.length <= 20000 +0 <= shifts[i] <= 10 ^ 9 +""" +from typing import List + + +class Solution: + def shiftingLetters(self, S: str, shifts: List[int]) -> str: + """ + preprocess shifts + """ + n = len(shifts) + for i in range(n-2, -1, -1): + shifts[i] += shifts[i+1] + shifts[i] %= 26 + + ret = [] + for i, s in enumerate(S): + b = (ord(s) + shifts[i] - ord('a')) % 26 + ord('a') + b = chr(b) + ret.append(b) + + print(ret) + return "".join(ret) + + +if __name__ == "__main__": + assert Solution().shiftingLetters("abc", [3, 5, 9]) == "rpl" From ca5f55bdc4bb4aaf998eab8da57aaab9de434d89 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 19 Mar 2019 23:39:27 -0700 Subject: [PATCH 160/344] 856 --- 848 Shifting Letters.py | 1 - 856 Score of Parentheses.py | 84 +++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 856 Score of Parentheses.py diff --git a/848 Shifting Letters.py b/848 Shifting Letters.py index c489459..be6eab9 100644 --- a/848 Shifting Letters.py +++ b/848 Shifting Letters.py @@ -44,7 +44,6 @@ def shiftingLetters(self, S: str, shifts: List[int]) -> str: b = chr(b) ret.append(b) - print(ret) return "".join(ret) diff --git a/856 Score of Parentheses.py b/856 Score of Parentheses.py new file mode 100644 index 0000000..764f01c --- /dev/null +++ b/856 Score of Parentheses.py @@ -0,0 +1,84 @@ +#!/usr/bin/python3 +""" +Given a balanced parentheses string S, compute the score of the string based on +the following rule: + +() has score 1 +AB has score A + B, where A and B are balanced parentheses strings. +(A) has score 2 * A, where A is a balanced parentheses string. + + +Example 1: + +Input: "()" +Output: 1 +Example 2: + +Input: "(())" +Output: 2 +Example 3: + +Input: "()()" +Output: 2 +Example 4: + +Input: "(()(()))" +Output: 6 + + +Note: + +S is a balanced parentheses string, containing only ( and ). +2 <= S.length <= 50 +""" + + +class Solution: + def scoreOfParentheses(self, S: str) -> int: + """ + stk + + Every position in the string has a depth - some number of matching + parentheses surrounding it + """ + stk = [] + ret = 0 + for s in S: + if s == "(": + stk.append(0) + else: + cur = stk.pop() + score = max(2 * cur, 1) + if stk: + stk[-1] += score + else: + ret += score + + return ret + + def scoreOfParentheses_error(self, S: str) -> int: + """ + stk + """ + ret = 0 + cur_stk = [] + for s in S: + if s == "(": + cur_stk.append(0) + stk.append(s) + else: + stk.pop() + if cur_stk[-1] == 0: + cur_stk[-1] = 1 + else: + cur_stk[-1] *= 2 + if not stk: + ret += cur + cur = 0 + + return ret + + +if __name__ == "__main__": + assert Solution().scoreOfParentheses("(())") == 2 + assert Solution().scoreOfParentheses("(()(()))") == 6 From 123bed0393630dc14c6ed43571f123571be5da8b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 20 Mar 2019 21:49:20 -0700 Subject: [PATCH 161/344] 859 --- 859 Buddy Strings.py | 70 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 859 Buddy Strings.py diff --git a/859 Buddy Strings.py b/859 Buddy Strings.py new file mode 100644 index 0000000..5cbddb0 --- /dev/null +++ b/859 Buddy Strings.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +""" +Given two strings A and B of lowercase letters, return true if and only if we +can swap two letters in A so that the result equals B. + + + +Example 1: + +Input: A = "ab", B = "ba" +Output: true +Example 2: + +Input: A = "ab", B = "ab" +Output: false +Example 3: + +Input: A = "aa", B = "aa" +Output: true +Example 4: + +Input: A = "aaaaaaabc", B = "aaaaaaacb" +Output: true +Example 5: + +Input: A = "", B = "aa" +Output: false + + +Note: + +0 <= A.length <= 20000 +0 <= B.length <= 20000 +A and B consist only of lowercase letters. +""" +USED = True + + +class Solution: + def buddyStrings(self, A: str, B: str) -> bool: + """ + iterate + """ + if len(A) != len(B): + return False + if A == B: + # find dup + seen = set() + for a in A: + if a in seen: + return True + seen.add(a) + else: + return False + + # Find a pair + pair = None + for i in range(len(A)): + if A[i] != B[i]: + if not pair: + pair = (A[i], B[i]) + elif pair == (B[i], A[i]): + pair = USED + else: + return False + + if pair is None or pair is USED: + return True + + return False From ea8f50b7399de8b9b39b156c657e4c30adb68ea3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 20 Mar 2019 21:54:42 -0700 Subject: [PATCH 162/344] 860 --- 860 Lemonade Change.py | 77 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 860 Lemonade Change.py diff --git a/860 Lemonade Change.py b/860 Lemonade Change.py new file mode 100644 index 0000000..35d44c4 --- /dev/null +++ b/860 Lemonade Change.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 +""" +At a lemonade stand, each lemonade costs $5. + +Customers are standing in a queue to buy from you, and order one at a time + (in the order specified by bills). + +Each customer will only buy one lemonade and pay with either a $5, $10, or $20 +bill. You must provide the correct change to each customer, so that the net +transaction is that the customer pays $5. + +Note that you don't have any change in hand at first. + +Return true if and only if you can provide every customer with correct change. + + + +Example 1: + +Input: [5,5,5,10,20] +Output: true +Explanation: +From the first 3 customers, we collect three $5 bills in order. +From the fourth customer, we collect a $10 bill and give back a $5. +From the fifth customer, we give a $10 bill and a $5 bill. +Since all customers got correct change, we output true. +Example 2: + +Input: [5,5,10] +Output: true +Example 3: + +Input: [10,10] +Output: false +Example 4: + +Input: [5,5,10,10,20] +Output: false +Explanation: +From the first two customers in order, we collect two $5 bills. +For the next two customers in order, we collect a $10 bill and give back a $5 +bill. +For the last customer, we can't give change of $15 back because we only have two +$10 bills. +Since not every customer received correct change, the answer is false. + +Note: + +0 <= bills.length <= 10000 +bills[i] will be either 5, 10, or 20. +""" + + +class Solution: + def lemonadeChange(self, bills: List[int]) -> bool: + """ + count + """ + five, ten, twenty = 0, 0, 0 + for b in bills: + if b == 5: + five += 1 + elif b == 10: + if five < 1: + return False + five -= 1 + ten += 1 + else: # 20 + if ten >= 1 and five >= 1: + ten -= 1 # ten first + five -= 1 + elif five >= 3: + five -= 3 + else: + return False + + return True From fd6968a169885d817b8b34e46cf810b81fa61ce4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 20 Mar 2019 22:48:48 -0700 Subject: [PATCH 163/344] 863 --- 863 All Nodes Distance K in Binary Tree.py | 140 +++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 863 All Nodes Distance K in Binary Tree.py diff --git a/863 All Nodes Distance K in Binary Tree.py b/863 All Nodes Distance K in Binary Tree.py new file mode 100644 index 0000000..13bdcea --- /dev/null +++ b/863 All Nodes Distance K in Binary Tree.py @@ -0,0 +1,140 @@ +#!/usr/bin/python3 +""" +We are given a binary tree (with root node root), a target node, and an integer +value K. + +Return a list of the values of all nodes that have a distance K from the target +node. The answer can be returned in any order. + + + +Example 1: + +Input: root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2 + +Output: [7,4,1] + +Explanation: +The nodes that are a distance 2 from the target node (with value 5) +have values 7, 4, and 1. + + + +Note that the inputs "root" and "target" are actually TreeNodes. +The descriptions of the inputs above are just serializations of these objects. + + +Note: + +The given tree is non-empty. +Each node in the tree has unique values 0 <= node.val <= 500. +The target node is a node in the tree. +0 <= K <= 1000. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def distanceK(self, root: TreeNode, target: TreeNode, K: int) -> List[int]: + """ + similar to SolutionComplicated + get its ancestor's distance, but at the same down go down through the tree + + O(N), vist each node 2 times + """ + ret = [] + self.ancestor_dist(root, K, target, ret) + return ret + + def dfs_down(self, node, d, ret): + """ + same as dfs1 + """ + if not node: + return + if d == 0: + ret.append(node.val) + else: + self.dfs_down(node.left, d - 1, ret) + self.dfs_down(node.right, d - 1, ret) + + def ancestor_dist(self, node, K, target, ret): + if not node: + return float('inf') + + if node.val == target.val: + # d = 0 + self.dfs_down(node, K, ret) + return 0 + else: + l = self.ancestor_dist(node.left, K, target, ret) + r = self.ancestor_dist(node.right, K, target, ret) + d = min(l, r) + 1 + if d == K: + ret.append(node.val) + elif l == float('inf'): + self.dfs_down(node.left, K - d - 1, ret) + else: # r == float('inf') + self.dfs_down(node.right, K - d - 1, ret) + return d + + +class SolutionComplicated: + def distanceK(self, root: TreeNode, target: TreeNode, K: int) -> List[int]: + """ + break the problem into two part + 1st problem: target's subtree - easy to solve + 2nd problem: mark parent, ancestor path length + """ + ret = [] + self.dfs1(target, K, ret) + hm = {} + self.ancestor_dist(root, target, hm) + self.dfs2(root, target, K, float("inf"), hm, ret) + return ret + + def dfs1(self, node, K, ret): + """1st problem""" + if not node: + return + + if K == 0: + ret.append(node.val) + else: + self.dfs1(node.left, K-1, ret) + self.dfs1(node.right, K-1, ret) + + def ancestor_dist(self, node, target, hm): + if not node: + return float('inf') + + if node.val == target.val: + hm[node.val] = 0 + else: + left = self.ancestor_dist(node.left, target, hm) + right = self.ancestor_dist(node.right, target, hm) + hm[node.val] = min(left, right) + 1 + + return hm[node.val] + + def dfs2(self, node, target, K, dist, hm, ret): + """2nd problem""" + if not node: + return + + if node.val == target.val: + return + + dist = min(dist, hm[node.val]) + if dist == K: + ret.append(node.val) + + self.dfs2(node.left, target, K, dist + 1, hm, ret) + self.dfs2(node.right, target, K, dist + 1, hm, ret) From 1a92ac0ca751af0f2792805c98baa04f5cd048ef Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 20 Mar 2019 23:35:30 -0700 Subject: [PATCH 164/344] 865 --- ...lest Subtree with all the Deepest Nodes.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 865 Smallest Subtree with all the Deepest Nodes.py diff --git a/865 Smallest Subtree with all the Deepest Nodes.py b/865 Smallest Subtree with all the Deepest Nodes.py new file mode 100644 index 0000000..c60f411 --- /dev/null +++ b/865 Smallest Subtree with all the Deepest Nodes.py @@ -0,0 +1,78 @@ +#!/usr/bin/python3 +""" +Given a binary tree rooted at root, the depth of each node is the shortest distance to the root. + +A node is deepest if it has the largest depth possible among any node in the entire tree. + +The subtree of a node is that node, plus the set of all descendants of that node. + +Return the node with the largest depth such that it contains all the deepest nodes in its subtree. + + + +Example 1: + +Input: [3,5,1,6,2,0,8,null,null,7,4] +Output: [2,7,4] +Explanation: + + +We return the node with value 2, colored in yellow in the diagram. +The nodes colored in blue are the deepest nodes of the tree. +The input "[3, 5, 1, 6, 2, 0, 8, null, null, 7, 4]" is a serialization of the given tree. +The output "[2, 7, 4]" is a serialization of the subtree rooted at the node with value 2. +Both the input and output have TreeNode type. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.deepest = -1 + self.deepest_nodes = None + self.ret = None + + def subtreeWithAllDeepest(self, root: TreeNode) -> TreeNode: + """ + lowest common ancestor of deepest node + """ + self.down(root, 0) + if len(self.deepest_nodes) == 1: + return self.deepest_nodes.pop() + + self.count(root) + return self.ret + + def down(self, node: TreeNode, d: int) -> None: + if not node: + return + + if d > self.deepest: + self.deepest = d + self.deepest_nodes = set([node]) + elif d == self.deepest: + self.deepest_nodes.add(node) + + self.down(node.left, d + 1) + self.down(node.right, d + 1) + + def count(self, node: TreeNode) -> int: + if not node: + return 0 + + l = self.count(node.left) + r = self.count(node.right) + if l != 0 and r != 0 and l + r == len(self.deepest_nodes): + self.ret = node + + count = l + r + if node in self.deepest_nodes: + count += 1 + return count From fbb3c2cbe5870e6e27bc4966a9b2b87ede8c97f9 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 21 Mar 2019 23:55:34 -0700 Subject: [PATCH 165/344] 870 --- 870 Advantage Shuffle.py | 68 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 870 Advantage Shuffle.py diff --git a/870 Advantage Shuffle.py b/870 Advantage Shuffle.py new file mode 100644 index 0000000..2d73718 --- /dev/null +++ b/870 Advantage Shuffle.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 +""" +Given two arrays A and B of equal size, the advantage of A with respect to B is +the number of indices i for which A[i] > B[i]. + +Return any permutation of A that maximizes its advantage with respect to B. + +Example 1: + +Input: A = [2,7,11,15], B = [1,10,4,11] +Output: [2,11,7,15] +Example 2: + +Input: A = [12,24,8,32], B = [13,25,32,11] +Output: [24,32,8,12] + + +Note: + +1 <= A.length = B.length <= 10000 +0 <= A[i] <= 10^9 +0 <= B[i] <= 10^9 +""" +from typing import List +from collections import defaultdict + + +class Solution: + def advantageCount(self, A: List[int], B: List[int]) -> List[int]: + """ + Gready select the smallest larger number + Then we need sort A + Iterate B and do a bisect on A? Hard to remove the chosen element on A + unless using a balanced BST + How about we sort B also? + Like a merge sort, compare both sorted A and sorted B + But we need to record the position of B's element since sorting break the + position + Keep a reverse index mapping is not enough, since duplicate in B + then keep a list + """ + idxes = defaultdict(list) + for i, b in enumerate(B): + idxes[b].append(i) + + n = len(A) + A.sort() + B.sort() + ret = [None for _ in range(n)] + not_used = [] + j = 0 + for a in A: + if a > B[j]: + i = idxes[B[j]].pop() + ret[i] = a + j += 1 + else: + not_used.append(a) + + for i in range(n): + if ret[i] is None: + ret[i] = not_used.pop() + + return ret + + +if __name__ == "__main__": + assert Solution().advantageCount([2,7,11,15], [1,10,4,11]) == [2,11,7,15] From a7620dcf7bc6b2a06b17e8cb5613c65a7c9cb5d9 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 22 Mar 2019 23:33:44 -0700 Subject: [PATCH 166/344] 869 --- 869 Reordered Power of 2.py | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 869 Reordered Power of 2.py diff --git a/869 Reordered Power of 2.py b/869 Reordered Power of 2.py new file mode 100644 index 0000000..6b4500d --- /dev/null +++ b/869 Reordered Power of 2.py @@ -0,0 +1,48 @@ +#!/usr/bin/python3 +""" +Starting with a positive integer N, we reorder the digits in any order (including the original order) such that the leading digit is not zero. + +Return true if and only if we can do this in a way such that the resulting number is a power of 2. + + + +Example 1: + +Input: 1 +Output: true +Example 2: + +Input: 10 +Output: false +Example 3: + +Input: 16 +Output: true +Example 4: + +Input: 24 +Output: false +Example 5: + +Input: 46 +Output: true + + +Note: + +1 <= N <= 10^9 +""" +from collections import Counter + + +class Solution: + def reorderedPowerOf2(self, N: int) -> bool: + """ + count the digit and compare + """ + counts = Counter(str(N)) + for i in range(31): # 32 bit unsighed int + if counts == Counter(str(1 << i)): + return True + else: + return False From 3127e6b2bb8005e88b461e7dff28f1757a9829c8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 23 Mar 2019 12:15:21 -0700 Subject: [PATCH 167/344] 873 --- ...Length of Longest Fibonacci Subsequence.py | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 873 Length of Longest Fibonacci Subsequence.py diff --git a/873 Length of Longest Fibonacci Subsequence.py b/873 Length of Longest Fibonacci Subsequence.py new file mode 100644 index 0000000..ce860b7 --- /dev/null +++ b/873 Length of Longest Fibonacci Subsequence.py @@ -0,0 +1,108 @@ +#!/usr/bin/python3 +""" +A sequence X_1, X_2, ..., X_n is fibonacci-like if: + +n >= 3 +X_i + X_{i+1} = X_{i+2} for all i + 2 <= n +Given a strictly increasing array A of positive integers forming a sequence, +find the length of the longest fibonacci-like subsequence of A. If one does not +exist, return 0. + +(Recall that a subsequence is derived from another sequence A by deleting any +number of elements (including none) from A, without changing the order of the +remaining elements. For example, [3, 5, 8] is a subsequence of [3, 4, 5, 6, 7, 8].) + + + +Example 1: + +Input: [1,2,3,4,5,6,7,8] +Output: 5 +Explanation: +The longest subsequence that is fibonacci-like: [1,2,3,5,8]. +Example 2: + +Input: [1,3,7,11,12,14,18] +Output: 3 +Explanation: +The longest subsequence that is fibonacci-like: +[1,11,12], [3,11,14] or [7,11,18]. + + +Note: + +3 <= A.length <= 1000 +1 <= A[0] < A[1] < ... < A[A.length - 1] <= 10^9 +(The time limit has been reduced by 50% for submissions in Java, C, and C++.) +""" +from typing import List + + +class Solution: + def lenLongestFibSubseq(self, A: List[int]) -> int: + """ + F[i][j] longest fib subsequence ending at A[i] with 2nd last element + A[j] + + F[k][i] = F[i][j] + 1 if A[i] + A[j] = A[k] + + O(N^2) * O(N) = O(N^3) + + can be optimized to O(N^2) by look forward + """ + n = len(A) + F = [[0 for _ in range(n)] for _ in range(n)] + for i in range(n): + F[i][i] = 1 + for j in range(i): + F[i][j] = 2 + + idxes = {} + for i in range(n): + idxes[A[i]] = i + + for i in range(n): + for j in range(i): + Ak = A[i] + A[j] + if Ak in idxes: + k = idxes[Ak] + F[k][i] = max(F[k][i], F[i][j] + 1) + + return max( + F[i][j] if F[i][j] > 2 else 0 + for i in range(n) + for j in range(i) + ) + + def lenLongestFibSubseq_TLE(self, A: List[int]) -> int: + """ + F[i][j] longest fib subsequence ending at A[i] with 2nd last element + A[j] + + F[k][i] = F[i][j] + 1 if A[i] + A[j] = A[k] + + O(N^2) * O(N) = O(N^3) + + can be optimized to O(N^2) by look forward + """ + n = len(A) + F = [[0 for _ in range(n)] for _ in range(n)] + for i in range(n): + F[i][i] = 1 + for j in range(i): + F[i][j] = 2 + + for k in range(n): + for i in range(k): + for j in range(i): + if A[i] + A[j] == A[k]: + F[k][i] = max(F[k][i], F[i][j] + 1) + + return max( + F[i][j] if F[i][j] > 2 else 0 + for i in range(n) + for j in range(i) + ) + +if __name__ == "__main__": + assert Solution().lenLongestFibSubseq([1,2,3,4,5,6,7,8]) == 5 From 60391e1651f11e364e973480acbfb03c0aa39b49 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 23 Mar 2019 14:39:41 -0700 Subject: [PATCH 168/344] 875 --- 875 Koko Eating Bananas.py | 69 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 875 Koko Eating Bananas.py diff --git a/875 Koko Eating Bananas.py b/875 Koko Eating Bananas.py new file mode 100644 index 0000000..91fae08 --- /dev/null +++ b/875 Koko Eating Bananas.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +""" +Koko loves to eat bananas. There are N piles of bananas, the i-th pile has +piles[i] bananas. The guards have gone and will come back in H hours. + +Koko can decide her bananas-per-hour eating speed of K. Each hour, she chooses +some pile of bananas, and eats K bananas from that pile. If the pile has less +than K bananas, she eats all of them instead, and won't eat any more bananas +during this hour. + +Koko likes to eat slowly, but still wants to finish eating all the bananas +before the guards come back. + +Return the minimum integer K such that she can eat all the bananas within H hours. + + + +Example 1: + +Input: piles = [3,6,7,11], H = 8 +Output: 4 +Example 2: + +Input: piles = [30,11,23,4,20], H = 5 +Output: 30 +Example 3: + +Input: piles = [30,11,23,4,20], H = 6 +Output: 23 + +Note: + +1 <= piles.length <= 10^4 +piles.length <= H <= 10^9 +1 <= piles[i] <= 10^9 +""" +from typing import List +import math + + +class Solution: + def minEatingSpeed(self, piles: List[int], H: int) -> int: + """ + validation: + each piles ceil(n/K) + + sum(ceil(piles[i]/K)) <= H + binary search + + O(log n * n) + """ + if len(piles) > H: + return None + + n = len(piles) + hi = max(piles) + 1 + lo = 1 + while lo < hi: + mid = (lo + hi) // 2 + if sum(math.ceil(piles[i] / mid) for i in range(n)) > H: + lo = mid + 1 + else: + hi = mid + + return lo + + +if __name__ == "__main__": + assert Solution().minEatingSpeed([3,6,7,11], 8) == 4 From 6598cdceafdc8b69930927effb254ebc716b5ab8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 23 Mar 2019 14:39:51 -0700 Subject: [PATCH 169/344] 876 --- 876 Middle of the Linked List.py | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 876 Middle of the Linked List.py diff --git a/876 Middle of the Linked List.py b/876 Middle of the Linked List.py new file mode 100644 index 0000000..2077e3a --- /dev/null +++ b/876 Middle of the Linked List.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +""" +Given a non-empty, singly linked list with head node head, return a middle node +of linked list. + +If there are two middle nodes, return the second middle node. + +Example 1: + +Input: [1,2,3,4,5] +Output: Node 3 from this list (Serialization: [3,4,5]) +The returned node has value 3. (The judge's serialization of this node is [3,4,5]). +Note that we returned a ListNode object ans, such that: +ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, and ans.next.next.next = NULL. +Example 2: + +Input: [1,2,3,4,5,6] +Output: Node 4 from this list (Serialization: [4,5,6]) +Since the list has two middle nodes with values 3 and 4, we return the second one. + +Note: + +The number of nodes in the given list will be between 1 and 100. +""" + + +# Definition for singly-linked list. +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + +class Solution: + def middleNode(self, head: ListNode) -> ListNode: + """ + """ + l = 0 + cur = head + while cur: + l += 1 + cur = cur.next + + mid = l // 2 + 1 + cur_l = 0 + cur = head + while cur: + cur_l += 1 + if cur_l == mid: + return cur + cur = cur.next + + return None From 5efe920f8a5049b3ad63e4024759fc3f0d58c314 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 23 Mar 2019 14:47:27 -0700 Subject: [PATCH 170/344] 884 --- 884 Uncommon Words from Two Sentences.py | 71 ++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 884 Uncommon Words from Two Sentences.py diff --git a/884 Uncommon Words from Two Sentences.py b/884 Uncommon Words from Two Sentences.py new file mode 100644 index 0000000..f79e524 --- /dev/null +++ b/884 Uncommon Words from Two Sentences.py @@ -0,0 +1,71 @@ +#!/usr/bin/python3 +""" +We are given two sentences A and B. (A sentence is a string of space separated +words. Each word consists only of lowercase letters.) + +A word is uncommon if it appears exactly once in one of the sentences, and does +not appear in the other sentence. + +Return a list of all uncommon words. + +You may return the list in any order. + + + +Example 1: + +Input: A = "this apple is sweet", B = "this apple is sour" +Output: ["sweet","sour"] +Example 2: + +Input: A = "apple apple", B = "banana" +Output: ["banana"] + + +Note: + +0 <= A.length <= 200 +0 <= B.length <= 200 +A and B both contain only spaces and lowercase letters. +""" +from typing import List +from collections import Counter + + +class Solution: + def uncommonFromSentences(self, A: str, B: str) -> List[str]: + """ + need counter, only need to appear once + """ + c = Counter(A.split()) + Counter(B.split()) + ret = [ + k + for k, v in c.items() + if v == 1 + ] + return ret + + def uncommonFromSentences_complext(self, A: str, B: str) -> List[str]: + """ + need counter + """ + c_A, c_B = Counter(A.split()), Counter(B.split()) + ret = [] + for k, v in c_A.items(): + if v == 1 and k not in c_B: + ret.append(k) + + for k, v in c_B.items(): + if v == 1 and k not in c_A: + ret.append(k) + + return ret + + def uncommonFromSentences_error(self, A: str, B: str) -> List[str]: + """ + set difference + """ + s_A, s_B = set(A.split()), set(B.split()) + return list( + (s_A - s_B) | (s_B - s_A) + ) From 6ecf6532b72943abea9b968045f97145aff9f904 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 23 Mar 2019 15:35:47 -0700 Subject: [PATCH 171/344] 881 --- 881 Boats to Save People.py | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 881 Boats to Save People.py diff --git a/881 Boats to Save People.py b/881 Boats to Save People.py new file mode 100644 index 0000000..b8c025e --- /dev/null +++ b/881 Boats to Save People.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" +The i-th person has weight people[i], and each boat can carry a maximum weight +of limit. + +Each boat carries at most 2 people at the same time, provided the sum of the +weight of those people is at most limit. + +Return the minimum number of boats to carry every given person. (It is +guaranteed each person can be carried by a boat.) + +Example 1: + +Input: people = [1,2], limit = 3 +Output: 1 +Explanation: 1 boat (1, 2) +Example 2: + +Input: people = [3,2,2,1], limit = 3 +Output: 3 +Explanation: 3 boats (1, 2), (2) and (3) +Example 3: + +Input: people = [3,5,3,4], limit = 5 +Output: 4 +Explanation: 4 boats (3), (3), (4), (5) +Note: + +1 <= people.length <= 50000 +1 <= people[i] <= limit <= 30000 +""" +from typing import List +from collections import deque + + +class Solution: + def numRescueBoats(self, people: List[int], limit: int) -> int: + """ + sort + gready + """ + ret = 0 + q = deque(sorted(people)) + while q: + tail = q.pop() + ret += 1 + if q and q[0] + tail <= limit: + q.popleft() + + return ret From ca1c3a67f2cdefd958cf25df9149efc217165069 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 23 Mar 2019 16:50:35 -0700 Subject: [PATCH 172/344] 880 --- 880 Decoded String at Index.py | 103 +++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 880 Decoded String at Index.py diff --git a/880 Decoded String at Index.py b/880 Decoded String at Index.py new file mode 100644 index 0000000..a03eda4 --- /dev/null +++ b/880 Decoded String at Index.py @@ -0,0 +1,103 @@ +#!/usr/bin/python3 +""" +An encoded string S is given. To find and write the decoded string to a tape, +the encoded string is read one character at a time and the following steps are +taken: + +If the character read is a letter, that letter is written onto the tape. +If the character read is a digit (say d), the entire current tape is repeatedly +written d-1 more times in total. +Now for some encoded string S, and an index K, find and return the K-th letter +(1 indexed) in the decoded string. + +Example 1: + +Input: S = "leet2code3", K = 10 +Output: "o" +Explanation: +The decoded string is "leetleetcodeleetleetcodeleetleetcode". +The 10th letter in the string is "o". +Example 2: + +Input: S = "ha22", K = 5 +Output: "h" +Explanation: +The decoded string is "hahahaha". The 5th letter is "h". +Example 3: + +Input: S = "a2345678999999999999999", K = 1 +Output: "a" +Explanation: +The decoded string is "a" repeated 8301530446056247680 times. The 1st letter is "a". + + +Note: + +2 <= S.length <= 100 +S will only contain lowercase letters and digits 2 through 9. +S starts with a letter. +1 <= K <= 10^9 +The decoded string is guaranteed to have less than 2^63 letters. +""" + + +class Solution: + def decodeAtIndex(self, S: str, K: int) -> str: + """ + walk backward + """ + l = 0 + for s in S: + if s.isdigit(): + l *= int(s) + else: + l += 1 + + # walk backward + for s in reversed(S): + K %= l + if K == 0 and s.isalpha(): + # K == l * n, return the last chr + return s + if s.isdigit(): + l //= int(s) + else: + l -= 1 + + raise + + def decodeAtIndex_error(self, S: str, K: int) -> str: + """ + don't generate the final string, too memory expensive + two pointer + + understanding error, one digit will make the entire str repeated + """ + K -= 1 # 0-indexed + i = 0 + j = 0 + last = None + n = len(S) + while j < n: + if S[j].isdigit(): + if not last: + last = j + + d = int(S[j]) + l = last - i + while K >= l and d > 0: + K -= l + d -= 1 + if d > 0: + return S[i + K] + elif last: + i = j + last = None + + j += 1 + + return S[i+K] + + +if __name__ == "__main__": + assert Solution().decodeAtIndex("ha22", 5) == "h" From 3649f75aad8dc3a7704766139f18a115bfc95bed Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 23 Mar 2019 17:53:30 -0700 Subject: [PATCH 173/344] 889 --- ...e from Preorder and Postorder Traversal.py | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 889 Construct Binary Tree from Preorder and Postorder Traversal.py diff --git a/889 Construct Binary Tree from Preorder and Postorder Traversal.py b/889 Construct Binary Tree from Preorder and Postorder Traversal.py new file mode 100644 index 0000000..5c15210 --- /dev/null +++ b/889 Construct Binary Tree from Preorder and Postorder Traversal.py @@ -0,0 +1,94 @@ +#!/usr/bin/python3 +""" +Return any binary tree that matches the given preorder and postorder traversals. + +Values in the traversals pre and post are distinct positive integers. + + + +Example 1: + +Input: pre = [1,2,4,5,3,6,7], post = [4,5,2,6,7,3,1] +Output: [1,2,3,4,5,6,7] + + +Note: + +1 <= pre.length == post.length <= 30 +pre[] and post[] are both permutations of 1, 2, ..., pre.length. +It is guaranteed an answer exists. If there exists multiple answers, you can +return any of them. +""" +from typing import List + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def constructFromPrePost(self, pre: List[int], post: List[int]) -> TreeNode: + """ + use stack + Preorder generate TreeNodes, push them to stack and postorder pop them out. + + Compare the stk[-1] with the currently scanning element in postorder, if + same, it means its subtree finish construction, pop it out. + + O(N) + """ + stk = [] + popped = None + j = 0 + for e in pre: + stk.append(TreeNode(e)) + while stk and stk[-1].val == post[j]: + popped = stk.pop() + j += 1 + if stk: + if not stk[-1].left: + stk[-1].left = popped + else: + stk[-1].right = popped + + assert j == len(post) + return popped # root is the last popped element + + def constructFromPrePost_complex(self, pre: List[int], post: List[int]) -> TreeNode: + """ + draw a full tree + pre order & post order + then see the pattern + + F(N) = 2 F(N/2) + O(N), then it is O(N logN) + """ + if not pre or not post: + return None + + root = TreeNode(pre[0]) + if len(pre) == 1: + return root + + if pre[1] == post[-2]: + # multiple answers + left = None + right = self.constructFromPrePost(pre[1:], post[:-1]) + else: + l = 0 + for a in post: + l += 1 + if a == pre[1]: + break + else: + raise + + left = self.constructFromPrePost(pre[1:1+l], post[:l]) + right = self.constructFromPrePost(pre[1+l:], post[l:-1]) + + root.left = left + root.right = right + return root From 0b2e9bd67f2f67e328130579f45bc51849ad8e88 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 23 Mar 2019 23:08:08 -0700 Subject: [PATCH 174/344] 894 --- 894 All Possible Full Binary Trees.py | 57 +++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 894 All Possible Full Binary Trees.py diff --git a/894 All Possible Full Binary Trees.py b/894 All Possible Full Binary Trees.py new file mode 100644 index 0000000..7b85a8c --- /dev/null +++ b/894 All Possible Full Binary Trees.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +""" +A full binary tree is a binary tree where each node has exactly 0 or 2 children. + +Return a list of all possible full binary trees with N nodes. Each element of +the answer is the root node of one possible tree. + +Each node of each tree in the answer must have node.val = 0. + +You may return the final list of trees in any order. + + + +Example 1: + +Input: 7 +Output: [[0,0,0,null,null,0,0,null,null,0,0],[0,0,0,null,null,0,0,0,0], +[0,0,0,0,0,0,0],[0,0,0,0,0,null,null,null,null,0,0],[0,0,0,0,0,null,null,0,0]] +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.cache = {} + + def allPossibleFBT(self, N: int) -> List[TreeNode]: + """ + recursive + memoization + """ + if N not in self.cache: + if N == 0: + ret = [] + elif N == 1: + ret = [TreeNode(0)] + else: + ret = [] + for i in range(N): + lefts = self.allPossibleFBT(i) + rights = self.allPossibleFBT(N-1-i) + # 0 or 2 child, cannot have only 1 + if lefts and rights: + for left in lefts: + for right in rights: + node = TreeNode(0) + node.left = left + node.right = right + ret.append(node) + self.cache[N] = ret + + return self.cache[N] From ed0f835c3c1d1e558e1fe660edd9abcb10966fa6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 24 Mar 2019 13:37:08 -0700 Subject: [PATCH 175/344] 896 --- 896 Monotonic Array.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 896 Monotonic Array.py diff --git a/896 Monotonic Array.py b/896 Monotonic Array.py new file mode 100644 index 0000000..e69de29 From 30e8e23272c5306902969c50e9dc35b1810c91aa Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 24 Mar 2019 13:37:40 -0700 Subject: [PATCH 176/344] 890 896 --- 890 Find and Replace Pattern.py | 60 +++++++++++++++++++++++++++++++++ 896 Monotonic Array.py | 58 +++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 890 Find and Replace Pattern.py diff --git a/890 Find and Replace Pattern.py b/890 Find and Replace Pattern.py new file mode 100644 index 0000000..de9a279 --- /dev/null +++ b/890 Find and Replace Pattern.py @@ -0,0 +1,60 @@ +#!/usr/bin/python3 +""" +You have a list of words and a pattern, and you want to know which words in +words matches the pattern. + +A word matches the pattern if there exists a permutation of letters p so that +after replacing every letter x in the pattern with p(x), we get the desired word. + +(Recall that a permutation of letters is a bijection from letters to letters: +every letter maps to another letter, and no two letters map to the same letter.) + +Return a list of the words in words that match the given pattern. + +You may return the answer in any order. + + + +Example 1: + +Input: words = ["abc","deq","mee","aqq","dkd","ccc"], pattern = "abb" +Output: ["mee","aqq"] +Explanation: "mee" matches the pattern because there is a permutation {a -> m, +b -> e, ...}. +"ccc" does not match the pattern because {a -> c, b -> c, ...} is not a +permutation, +since a and b map to the same letter. + +Note: + +1 <= words.length <= 50 +1 <= pattern.length = words[i].length <= 20 +""" +from typing import List + + +class Solution: + def findAndReplacePattern(self, words: List[str], pattern: str) -> List[str]: + """ + mapping + """ + ret = [] + for w in words: + if self.match(w, pattern): + ret.append(w) + return ret + + def match(self, word, pattern): + if len(word) != len(pattern): + return False + + m = {} + m_inv = {} # bijection + for i in range(len(word)): + if word[i] not in m and pattern[i] not in m_inv: + m[word[i]] = pattern[i] + m_inv[pattern[i]] = word[i] + elif word[i] not in m or m[word[i]] != pattern[i]: + return False + else: + return True diff --git a/896 Monotonic Array.py b/896 Monotonic Array.py index e69de29..2833bc5 100644 --- a/896 Monotonic Array.py +++ b/896 Monotonic Array.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 +""" +An array is monotonic if it is either monotone increasing or monotone +decreasing. + +An array A is monotone increasing if for all i <= j, A[i] <= A[j]. An array A +is monotone decreasing if for all i <= j, A[i] >= A[j]. + +Return true if and only if the given array A is monotonic. + + + +Example 1: + +Input: [1,2,2,3] +Output: true +Example 2: + +Input: [6,5,4,4] +Output: true +Example 3: + +Input: [1,3,2] +Output: false +Example 4: + +Input: [1,2,4,5] +Output: true +Example 5: + +Input: [1,1,1] +Output: true + + +Note: + +1 <= A.length <= 50000 +-100000 <= A[i] <= 100000 +""" +from typing import List + + +class Solution: + def isMonotonic(self, A: List[int]) -> bool: + mono = 0 # 0 undecided, 1 decr, 2 incr + for i in range(1, len(A)): + if mono == 0: + if A[i] > A[i-1]: + mono = 2 + elif A[i] < A[i-1]: + mono = 1 + else: + if A[i] > A[i-1] and mono == 1: + return False + elif A[i] < A[i-1] and mono == 2: + return False + else: + return True From 46d62ce0a975d2dbc4f7e357f50e1e3b3057d5b5 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 24 Mar 2019 14:07:21 -0700 Subject: [PATCH 177/344] 898 --- 898 Bitwise ORs of Subarrays.py | 60 +++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 898 Bitwise ORs of Subarrays.py diff --git a/898 Bitwise ORs of Subarrays.py b/898 Bitwise ORs of Subarrays.py new file mode 100644 index 0000000..e686977 --- /dev/null +++ b/898 Bitwise ORs of Subarrays.py @@ -0,0 +1,60 @@ +#!/usr/bin/python3 +""" +We have an array A of non-negative integers. + +For every (contiguous) subarray B = [A[i], A[i+1], ..., A[j]] (with i <= j), we +take the bitwise OR of all the elements in B, obtaining a result A[i] | A[i+1] +| ... | A[j]. + +Return the number of possible results. (Results that occur more than once are +only counted once in the final answer.) + + + +Example 1: + +Input: [0] +Output: 1 +Explanation: +There is only one possible result: 0. +Example 2: + +Input: [1,1,2] +Output: 3 +Explanation: +The possible subarrays are [1], [1], [2], [1, 1], [1, 2], [1, 1, 2]. +These yield the results 1, 1, 2, 1, 3, 3. +There are 3 unique values, so the answer is 3. +Example 3: + +Input: [1,2,4] +Output: 6 +Explanation: +The possible results are 1, 2, 3, 4, 6, and 7. + + +Note: + +1 <= A.length <= 50000 +0 <= A[i] <= 10^9 +""" + +class Solution: + def subarrayBitwiseORs(self, A: List[int]) -> int: + """ + Use a dp array to record OR + F[i][j] + O(N^2) TLE + + F[i][j] records the list of the results + #F[i][j] >= #F[i+1][j] >= #F[i+2][j] since it is monotonously increasing + The increasing part is by having one more 1 in the bit of any 32 bit of int + then F[i][j] is at most O(32) + """ + ret = set() + cur = set() # F[0][i] + for a in A: + cur = {a | e for e in cur} | {a} + ret |= cur + + return len(ret) From 44d6309fe1dfcc2ad6a28ff8337017e4505975b3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 24 Mar 2019 14:09:55 -0700 Subject: [PATCH 178/344] 905 --- 905 Sort Array By Parity.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 905 Sort Array By Parity.py diff --git a/905 Sort Array By Parity.py b/905 Sort Array By Parity.py new file mode 100644 index 0000000..40f754b --- /dev/null +++ b/905 Sort Array By Parity.py @@ -0,0 +1,36 @@ +#!/usr/bin/python3 +""" +Given an array A of non-negative integers, return an array consisting of all the +even elements of A, followed by all the odd elements of A. + +You may return any answer array that satisfies this condition. + + + +Example 1: + +Input: [3,1,2,4] +Output: [2,4,3,1] +The outputs [4,2,3,1], [2,4,1,3], and [4,2,1,3] would also be accepted. + + +Note: + +1 <= A.length <= 5000 +0 <= A[i] <= 5000 +""" +from typing import List + + +class Solution: + def sortArrayByParity(self, A: List[int]) -> List[int]: + """ + pointer + """ + closed = -1 + for i in range(len(A)): + if A[i] % 2 == 0: + closed += 1 + A[closed], A[i] = A[i], A[closed] + + return A From 68836d6b9aca9b9b0ffde6f34cc1b188f88fc12a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 24 Mar 2019 21:23:39 -0700 Subject: [PATCH 179/344] 907 --- 907 Sum of Subarray Minimums.py | 104 ++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 907 Sum of Subarray Minimums.py diff --git a/907 Sum of Subarray Minimums.py b/907 Sum of Subarray Minimums.py new file mode 100644 index 0000000..7d6db3f --- /dev/null +++ b/907 Sum of Subarray Minimums.py @@ -0,0 +1,104 @@ +#!/usr/bin/python3 +""" +Given an array of integers A, find the sum of min(B), where B ranges over every +(contiguous) subarray of A. + +Since the answer may be large, return the answer modulo 10^9 + 7. + + + +Example 1: + +Input: [3,1,2,4] +Output: 17 +Explanation: Subarrays are [3], [1], [2], [4], [3,1], [1,2], [2,4], [3,1,2], +[1,2,4], [3,1,2,4]. +Minimums are 3, 1, 2, 4, 1, 1, 2, 1, 1, 1. Sum is 17. + + +Note: + +1 <= A.length <= 30000 +1 <= A[i] <= 30000 +""" +from typing import List + +MOD = 10 ** 9 + 7 + + +class Solution: + def sumSubarrayMins(self, A: List[int]) -> int: + """ + Let F[i][j] be the min of A[i:j] + O(N^2) + + There are a number of subarray with min as A[i] + A[m].... A[i] ... A[n] + A[m] < A[i] + A[n] < A[i] + then min(A[m+1:n]) is A[i] + + a a A[i] a + 3 choices on the left, 2 choices on the right + totally 3 * 2 = 6 subarrays + + use an increasing stk from both left and right + L[i] records the index of m, default -1 + R[i] records the index of n, default len(A) + """ + n = len(A) + L = [-1 for _ in A] + R = [n for _ in A] + + stk = [] + for i in range(n): + while stk and A[stk[-1]] >= A[i]: + stk.pop() + + if stk: + L[i] = stk[-1] + stk.append(i) + + stk = [] + for i in range(n-1, -1, -1): + # avoid double count when equal, attribtue to leftmost duplicate + while stk and A[stk[-1]] > A[i]: + stk.pop() + + if stk: + R[i] = stk[-1] + stk.append(i) + + ret = 0 + for i in range(n): + ret += ( + A[i] * (i - L[i]) * (R[i] - i) + ) + ret %= MOD + + return ret + + +class Solution: + def sumSubarrayMins(self, A: List[int]) -> int: + """ + Improve the above solution using one stack + use an increasing stk + """ + stk = [] + A = [-float('inf')] + A + [-float('inf')] + ret = 0 + for i, a in enumerate(A): + while stk and A[stk[-1]] > a: + h = stk.pop() + # record for h + ret += A[h] * (h - stk[-1]) * (i - h) + ret %= MOD + + stk.append(i) + return ret + + +if __name__ == "__main__": + assert Solution().sumSubarrayMins([71,55,82,55]) == 593 + assert Solution().sumSubarrayMins([3,1,2,4]) == 17 From 9f154d0dd8bccf66b32d388e370540c9043b352d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 25 Mar 2019 22:32:41 -0700 Subject: [PATCH 180/344] 915 --- ...Partition Array into Disjoint Intervals.py | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 915 Partition Array into Disjoint Intervals.py diff --git a/915 Partition Array into Disjoint Intervals.py b/915 Partition Array into Disjoint Intervals.py new file mode 100644 index 0000000..4ffd3d8 --- /dev/null +++ b/915 Partition Array into Disjoint Intervals.py @@ -0,0 +1,71 @@ +#!/usr/bin/python3 +""" +Given an array A, partition it into two (contiguous) subarrays left and right so that: + +Every element in left is less than or equal to every element in right. +left and right are non-empty. +left has the smallest possible size. +Return the length of left after such a partitioning. It is guaranteed that such a partitioning exists. + + + +Example 1: + +Input: [5,0,3,8,6] +Output: 3 +Explanation: left = [5,0,3], right = [8,6] +Example 2: + +Input: [1,1,1,0,6,12] +Output: 4 +Explanation: left = [1,1,1,0], right = [6,12] + + +Note: + +2 <= A.length <= 30000 +0 <= A[i] <= 10^6 +It is guaranteed there is at least one way to partition A as described. +""" +from typing import List + + +class Solution: + def partitionDisjoint(self, A: List[int]) -> int: + """ + max(left) <= min(right) + + similar to 2 in terms of keyboard stroke count + """ + n = len(A) + MX = [-float('inf') for _ in range(n+1)] + MI = [float('inf') for _ in range(n+1)] + for i in range(n): + MX[i+1] = max(M[i], A[i]) + for i in range(n-1, -1, -1): + MI[i] = min(MI[i+1], A[i]) + + for l in range(1, n+1): + if MX[l] <= MI[l]: + return l + raise + + def partitionDisjoint_2(self, A: List[int]) -> int: + """ + max(left) <= min(right) + """ + MX = [0 for _ in A] + MI = [0 for _ in A] + MX[0] = A[0] + MI[-1] = A[-1] + n = len(A) + for i in range(1, n): + MX[i] = max(MX[i-1], A[i]) + for i in range(n-2, -1, -1): + MI[i] = min(MI[i+1], A[i]) + + for i in range(n-1): + if MX[i] <= MI[i+1]: + return i + + raise From 31359ea38f14cba507aa6651699ba2c05aad783b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 25 Mar 2019 22:57:38 -0700 Subject: [PATCH 181/344] 916 --- 916 Word Subsets.py | 75 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 916 Word Subsets.py diff --git a/916 Word Subsets.py b/916 Word Subsets.py new file mode 100644 index 0000000..94c1b8b --- /dev/null +++ b/916 Word Subsets.py @@ -0,0 +1,75 @@ +#!/usr/bin/python3 +""" +We are given two arrays A and B of words. Each word is a string of lowercase +letters. + +Now, say that word b is a subset of word a if every letter in b occurs in a, +including multiplicity. For example, "wrr" is a subset of "warrior", but is +not a subset of "world". + +Now say a word a from A is universal if for every b in B, b is a subset of a. + +Return a list of all universal words in A. You can return the words in any order. + + + +Example 1: + +Input: A = ["amazon","apple","facebook","google","leetcode"], B = ["e","o"] +Output: ["facebook","google","leetcode"] +Example 2: + +Input: A = ["amazon","apple","facebook","google","leetcode"], B = ["l","e"] +Output: ["apple","google","leetcode"] +Example 3: + +Input: A = ["amazon","apple","facebook","google","leetcode"], B = ["e","oo"] +Output: ["facebook","google"] +Example 4: + +Input: A = ["amazon","apple","facebook","google","leetcode"], B = ["lo","eo"] +Output: ["google","leetcode"] +Example 5: + +Input: A = ["amazon","apple","facebook","google","leetcode"], B = ["ec","oc","ceo"] +Output: ["facebook","leetcode"] + + +Note: + +1 <= A.length, B.length <= 10000 +1 <= A[i].length, B[i].length <= 10 +A[i] and B[i] consist only of lowercase letters. +All words in A[i] are unique: there isn't i != j with A[i] == A[j]. +""" +from typing import List +from collections import Counter, defaultdict + + +class Solution: + def wordSubsets(self, A: List[str], B: List[str]) -> List[str]: + """ + brute foce check b subset of a: two pointers O(|a| + |b|) + O(n * m * (|a|+|b|)) + + The order of chars does not matter. + + For every letter + C_letter (a) >= max(C_letter(b) for b in B) + """ + mx = defaultdict(int) + for b in B: + c = Counter(b) + for k, v in c.items(): + mx[k] = max(mx[k], v) + + ret = [] + for a in A: + c = Counter(a) + for k, v in mx.items(): + if c[k] < v: + break + else: + ret.append(a) + + return ret From 713ca012be0b8e8c802c3ba0ef853a746f3f0203 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Mar 2019 23:48:13 -0700 Subject: [PATCH 182/344] 918 --- 918 Maximum Sum Circular Subarray.py | 97 ++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 918 Maximum Sum Circular Subarray.py diff --git a/918 Maximum Sum Circular Subarray.py b/918 Maximum Sum Circular Subarray.py new file mode 100644 index 0000000..aa16e1e --- /dev/null +++ b/918 Maximum Sum Circular Subarray.py @@ -0,0 +1,97 @@ +#!/usr/bin/python3 +""" +Given a circular array C of integers represented by A, find the maximum possible +sum of a non-empty subarray of C. + +Here, a circular array means the end of the array connects to the beginning of +the array. (Formally, C[i] = A[i] when 0 <= i < A.length, and C[i+A.length] = +C[i] when i >= 0.) + +Also, a subarray may only include each element of the fixed buffer A at most +once. (Formally, for a subarray C[i], C[i+1], ..., C[j], there does not exist +i <= k1, k2 <= j with k1 % A.length = k2 % A.length.) + + + +Example 1: + +Input: [1,-2,3,-2] +Output: 3 +Explanation: Subarray [3] has maximum sum 3 +Example 2: + +Input: [5,-3,5] +Output: 10 +Explanation: Subarray [5,5] has maximum sum 5 + 5 = 10 +Example 3: + +Input: [3,-1,2,-1] +Output: 4 +Explanation: Subarray [2,-1,3] has maximum sum 2 + (-1) + 3 = 4 +Example 4: + +Input: [3,-2,2,-3] +Output: 3 +Explanation: Subarray [3] and [3,-2,2] both have maximum sum 3 +Example 5: + +Input: [-2,-3,-1] +Output: -1 +Explanation: Subarray [-1] has maximum sum -1 + + +Note: + +-30000 <= A[i] <= 30000 +1 <= A.length <= 30000 +""" +from typing import List + + +class Solution: + def maxSubarraySumCircular(self, A: List[int]) -> int: + """ + Kadane's Algorithm + Two cases: + 1. normal max subarray within A + 2. circular one, that both A[0] and A[n-1] is included + (A0 + A1 + .. + Ai) + (Aj + ... + An-1) + = sum(A) - (Ai+1 + ... + Aj-1) + """ + ret1 = self.max_subarray(A) + ret2 = sum(A) + self.max_subarray([-a for a in A[1:-1]]) # max negative (-1) + return max(ret1, ret2) + + def max_subarray(self, A) -> int: + """ + dp[i] = A[i] + max(dp[i-1],0) + """ + mx = -float('inf') + cur = 0 + for a in A: + cur = a + max(cur, 0) # RHS cur is the prev + mx = max(mx, cur) + return mx + + def maxSubarraySumCircular_error(self, A: List[int]) -> int: + """ + keep a cur_sum with index, when negative, go back to 0 + """ + cur = [0, None] + mx = -float('inf') + i = 0 + j = 0 + n = len(A) + while i < n: + cur[0] += A[i] + cur[1] = i + mx = max(mx, cur[0]) + j = i + 1 + while cur[0] >= 0 and j < i + n: + cur[0] += A[j % n] + mx = max(mx, cur[0]) + j += 1 + + i = j + + return mx From a852e8f8e75abe9c2d78045f07529c8fbbc010bf Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 27 Mar 2019 22:44:38 -0700 Subject: [PATCH 183/344] 922 --- 922 Sort Array By Parity II.py | 63 ++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 922 Sort Array By Parity II.py diff --git a/922 Sort Array By Parity II.py b/922 Sort Array By Parity II.py new file mode 100644 index 0000000..d3037fb --- /dev/null +++ b/922 Sort Array By Parity II.py @@ -0,0 +1,63 @@ +#!/usr/bin/python3 +""" +Given an array A of non-negative integers, half of the integers in A are odd, +and half of the integers are even. + +Sort the array so that whenever A[i] is odd, i is odd; and whenever A[i] is even +, i is even. + +You may return any answer array that satisfies this condition. + + + +Example 1: + +Input: [4,2,5,7] +Output: [4,5,2,7] +Explanation: [4,7,2,5], [2,5,4,7], [2,7,4,5] would also have been accepted. + + +Note: + +2 <= A.length <= 20000 +A.length % 2 == 0 +0 <= A[i] <= 1000 +""" +from typing import List + + +class Solution: + def sortArrayByParityII(self, A: List[int]) -> List[int]: + even_idx = 0 + for odd_idx in range(1, len(A), 2): + if A[odd_idx] % 2 == 0: + while A[even_idx] % 2 == 0: + even_idx += 2 + A[odd_idx], A[even_idx] = A[even_idx], A[odd_idx] + + return A + + + def sortArrayByParityII_complex(self, A: List[int]) -> List[int]: + """ + in-place two passes + """ + closed = -1 + n = len(A) + for i in range(n): + if A[i] % 2 == 0: + closed += 1 + A[i], A[closed] = A[closed], A[i] + + j = closed + 1 + if j % 2 == 1: + j += 1 + for i in range(1, closed + 1, 2): + A[i], A[j] = A[j], A[i] + j += 2 + + return A + + +if __name__ == "__main__": + assert Solution().sortArrayByParityII([4,1,1,0,1,0]) == [4,1,0,1,0,1] From 9306ca82ee824a1724f1b0642a6701de92b05475 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 27 Mar 2019 22:51:50 -0700 Subject: [PATCH 184/344] 921 --- 921 Minimum Add to Make Parentheses Valid.py | 58 ++++++++++++++++++++ 922 Sort Array By Parity II.py | 1 - 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 921 Minimum Add to Make Parentheses Valid.py diff --git a/921 Minimum Add to Make Parentheses Valid.py b/921 Minimum Add to Make Parentheses Valid.py new file mode 100644 index 0000000..6ac0c02 --- /dev/null +++ b/921 Minimum Add to Make Parentheses Valid.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 +""" +Given a string S of '(' and ')' parentheses, we add the minimum number of +parentheses ( '(' or ')', and in any positions ) so that the resulting +parentheses string is valid. + +Formally, a parentheses string is valid if and only if: + +It is the empty string, or +It can be written as AB (A concatenated with B), where A and B are valid +strings, or +It can be written as (A), where A is a valid string. +Given a parentheses string, return the minimum number of parentheses we must add +to make the resulting string valid. + +Example 1: + +Input: "())" +Output: 1 +Example 2: + +Input: "(((" +Output: 3 +Example 3: + +Input: "()" +Output: 0 +Example 4: + +Input: "()))((" +Output: 4 + + +Note: + +S.length <= 1000 +S only consists of '(' and ')' characters. +""" + + +class Solution: + def minAddToMakeValid(self, S: str) -> int: + """ + stk + """ + ret = 0 + stk = [] + for s in S: + if s == "(": + stk.append(s) + else: + if stk: + stk.pop() + else: + ret += 1 + + ret += len(stk) + return ret diff --git a/922 Sort Array By Parity II.py b/922 Sort Array By Parity II.py index d3037fb..866993f 100644 --- a/922 Sort Array By Parity II.py +++ b/922 Sort Array By Parity II.py @@ -37,7 +37,6 @@ def sortArrayByParityII(self, A: List[int]) -> List[int]: return A - def sortArrayByParityII_complex(self, A: List[int]) -> List[int]: """ in-place two passes From e9b8cb8d19a29471aba0bf371efa095f0991427a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 27 Mar 2019 23:44:30 -0700 Subject: [PATCH 185/344] 926 --- 926 Flip String to Monotone Increasing.py | 62 +++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 926 Flip String to Monotone Increasing.py diff --git a/926 Flip String to Monotone Increasing.py b/926 Flip String to Monotone Increasing.py new file mode 100644 index 0000000..7c16d23 --- /dev/null +++ b/926 Flip String to Monotone Increasing.py @@ -0,0 +1,62 @@ +#!/usr/bin/python3 +""" +A string of '0's and '1's is monotone increasing if it consists of some number +of '0's (possibly 0), followed by some number of '1's (also possibly 0.) + +We are given a string S of '0's and '1's, and we may flip any '0' to a '1' or a +'1' to a '0'. + +Return the minimum number of flips to make S monotone increasing. + + + +Example 1: + +Input: "00110" +Output: 1 +Explanation: We flip the last digit to get 00111. +Example 2: + +Input: "010110" +Output: 2 +Explanation: We flip to get 011111, or alternatively 000111. +Example 3: + +Input: "00011000" +Output: 2 +Explanation: We flip to get 00000000. + + +Note: + +1 <= S.length <= 20000 +S only consists of '0' and '1' characters. +""" + + +class Solution: + def minFlipsMonoIncr(self, S: str) -> int: + """ + let S[i] be the flipping point, leftside 0, rightside 1 + count number of 1 from the left, + count number of 0 from the right + O(N) + """ + n = len(S) + Z = [0 for _ in range(n+1)] # let Z[i] be #zero in A[i:] + O = [0 for _ in range(n+1)] # let O[i] be #one in A[:i] + for i in range(1, n+1): + O[i] = O[i-1] + if S[i-1] == "1": + O[i] += 1 + + for i in range(n-1, -1, -1): + Z[i] = Z[i+1] + if S[i] == "0": + Z[i] += 1 + + ret = float('inf') + for i in range(n): + ret = min(ret, O[i] + Z[i+1]) + + return ret From e849135574b0e5eec416fd18f9c3183fc3bc9883 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 28 Mar 2019 14:35:32 -0700 Subject: [PATCH 186/344] 437 --- 437 Path Sum III.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/437 Path Sum III.py b/437 Path Sum III.py index 1fa2da8..c7ad316 100644 --- a/437 Path Sum III.py +++ b/437 Path Sum III.py @@ -18,7 +18,41 @@ def __init__(self, x): self.right = None +from collections import defaultdict + + class Solution: + def __init__(self): + self.count = 0 + + def pathSum(self, root: TreeNode, target: int) -> int: + """ + The path does not need to start or end at the root or a leaf, but it + must go downwards (traveling only from parent nodes to child nodes). + + Downward path + """ + self.dfs(root, target, 0, defaultdict(int)) + return self.count + + def dfs(self, node, target, cur_sum, prefix_sum_counter): + if not node: + return + + cur_sum += node.val + # delta = target - cur_sum # error + delta = cur_sum - target + self.count += prefix_sum_counter[delta] + if delta == 0: + self.count += 1 + + prefix_sum_counter[cur_sum] += 1 + self.dfs(node.left, target, cur_sum, prefix_sum_counter) + self.dfs(node.right, target, cur_sum, prefix_sum_counter) + prefix_sum_counter[cur_sum] -= 1 + + +class SolutionComplex: def pathSum(self, root, sum): """ Brute force: two dfs, O(n^2) From a5ab611ebca52002e85c1f6d14fdd42bba7d30d9 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 28 Mar 2019 18:30:15 -0700 Subject: [PATCH 187/344] 930 --- 930 Binary Subarrays With Sum.py | 89 ++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 930 Binary Subarrays With Sum.py diff --git a/930 Binary Subarrays With Sum.py b/930 Binary Subarrays With Sum.py new file mode 100644 index 0000000..ec20f34 --- /dev/null +++ b/930 Binary Subarrays With Sum.py @@ -0,0 +1,89 @@ +#!/usr/bin/python3 +""" +In an array A of 0s and 1s, how many non-empty subarrays have sum S? + + + +Example 1: + +Input: A = [1,0,1,0,1], S = 2 +Output: 4 +Explanation: +The 4 subarrays are bolded below: +[1,0,1,0,1] +[1,0,1,0,1] +[1,0,1,0,1] +[1,0,1,0,1] + + +Note: + +A.length <= 30000 +0 <= S <= A.length +A[i] is either 0 or 1. +""" +from typing import List + + +class Solution: + def numSubarraysWithSum(self, A: List[int], S: int) -> int: + """ + Two pointers + i_lo and i_hi + count = i_hi - i_lo + 1 + """ + ret = 0 + i_lo, i_hi, j = 0, 0, 0 + sum_lo, sum_hi = 0, 0 + for j in range(len(A)): + sum_lo += A[j] + sum_hi += A[j] + while i_lo < j and sum_lo > S: + sum_lo -= A[i_lo] + i_lo += 1 + while i_hi < j and (sum_hi > S or sum_hi == S and A[i_hi] == 0): + sum_hi -= A[i_hi] + i_hi += 1 + assert i_hi >= i_lo + if sum_lo == S: + assert sum_hi == S + ret += i_hi - i_lo + 1 + + return ret + + def numSubarraysWithSum_error(self, A: List[int], S: int) -> int: + """ + Continuous subarrays sum using prefix sum to target O(N), space O(N) + Two pointer, O(N), space O(1) + """ + ret = 0 + i = 0 + j = 0 + n = len(A) + cur_sum = 0 + while j < n: + cur_sum += A[j] + if cur_sum < S and j < n: + j += 1 + elif cur_sum == S: + ret += 1 + while i <= j and A[i] == 0: + i += 1 + ret += 1 + j += 1 + else: + while i <= j and cur_sum > S: + cur_sum -= A[i] + i += 1 + if cur_sum == S: + ret += 1 + while i <= j and A[i] == 0: + i += 1 + ret += 1 + j += 1 + + return ret + + +if __name__ == "__main__": + assert Solution().numSubarraysWithSum([1,0,1,0,1], 2) == 4 From 497b832a80649fa67d68f410c718b3e835e627fb Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 28 Mar 2019 18:45:27 -0700 Subject: [PATCH 188/344] 931 --- 930 Binary Subarrays With Sum.py | 2 - 931 Minimum Falling Path Sum.py | 76 ++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 931 Minimum Falling Path Sum.py diff --git a/930 Binary Subarrays With Sum.py b/930 Binary Subarrays With Sum.py index ec20f34..f58b19e 100644 --- a/930 Binary Subarrays With Sum.py +++ b/930 Binary Subarrays With Sum.py @@ -2,8 +2,6 @@ """ In an array A of 0s and 1s, how many non-empty subarrays have sum S? - - Example 1: Input: A = [1,0,1,0,1], S = 2 diff --git a/931 Minimum Falling Path Sum.py b/931 Minimum Falling Path Sum.py new file mode 100644 index 0000000..85cad4d --- /dev/null +++ b/931 Minimum Falling Path Sum.py @@ -0,0 +1,76 @@ +#!/usr/bin/python3 +""" +Given a square array of integers A, we want the minimum sum of a falling path +through A. + +A falling path starts at any element in the first row, and chooses one element +from each row. The next row's choice must be in a column that is different from +the previous row's column by at most one. + + + +Example 1: + +Input: [[1,2,3],[4,5,6],[7,8,9]] +Output: 12 +Explanation: +The possible falling paths are: +[1,4,7], [1,4,8], [1,5,7], [1,5,8], [1,5,9] +[2,4,7], [2,4,8], [2,5,7], [2,5,8], [2,5,9], [2,6,8], [2,6,9] +[3,5,7], [3,5,8], [3,5,9], [3,6,8], [3,6,9] +The falling path with the smallest sum is [1,4,7], so the answer is 12. + + + +Note: + +1 <= A.length == A[0].length <= 100 +-100 <= A[i][j] <= 100 +""" +from typing import List +from collections import defaultdict + +class Solution: + def minFallingPathSum(self, A: List[List[int]]) -> int: + """ + dp, build from bottom + let F[i][j] be the min falling path sum at A[i][j] + using default dict + """ + m, n = len(A), len(A[0]) + F = defaultdict(lambda: defaultdict(lambda: float("inf"))) + for j in range(n): + F[m-1][j] = A[m-1][j] + + for i in range(m-2, -1, -1): + for j in range(n): + F[i][j] = min(F[i+1][j-1], F[i+1][j], F[i+1][j+1]) + A[i][j] + + return min( + F[0][j] + for j in range(n) + ) + + def minFallingPathSum_std(self, A: List[List[int]]) -> int: + """ + dp, build from bottom + let F[i][j] be the min falling path sum at A[i][j] + """ + m, n = len(A), len(A[0]) + F = [[float('inf') for _ in range(n)] for _ in range(m)] + for j in range(n): + F[m-1][j] = A[m-1][j] + + for i in range(m-2, -1, -1): + for j in range(n): + F[i][j] = min(F[i][j], F[i+1][j] + A[i][j]) + if j - 1 >= 0: + F[i][j] = min(F[i][j], F[i+1][j-1] + A[i][j]) + if j + 1 < n: + F[i][j] = min(F[i][j], F[i+1][j+1] + A[i][j]) + + return min(F[0]) + + +if __name__ == "__main__": + assert Solution().minFallingPathSum([[1,2,3],[4,5,6],[7,8,9]]) == 12 From 22dfe446880dbd49a15f6bc23bebedee1ac8a81f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 28 Mar 2019 19:38:46 -0700 Subject: [PATCH 189/344] 938 --- 938 Range Sum of BST.py | 56 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 938 Range Sum of BST.py diff --git a/938 Range Sum of BST.py b/938 Range Sum of BST.py new file mode 100644 index 0000000..2fb7a9e --- /dev/null +++ b/938 Range Sum of BST.py @@ -0,0 +1,56 @@ +#!/usr/bin/python3 +""" +Given the root node of a binary search tree, return the sum of values of all +nodes with value between L and R (inclusive). + +The binary search tree is guaranteed to have unique values. + +Example 1: + +Input: root = [10,5,15,3,7,null,18], L = 7, R = 15 +Output: 32 +Example 2: + +Input: root = [10,5,15,3,7,13,18,1,null,6], L = 6, R = 10 +Output: 23 + + +Note: + +The number of nodes in the tree is at most 10000. +The final answer is guaranteed to be less than 2^31. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.ret = 0 + + def rangeSumBST(self, root: TreeNode, L: int, R: int) -> int: + """ + traverse + """ + self.dfs(root, L, R) + return self.ret + + def dfs(self, node, L, R): + if not node: + return + + if L <= node.val <= R: + self.ret += node.val + self.dfs(node.left, L, R) + self.dfs(node.right, L, R) + + elif node.val > R: + self.dfs(node.left, L, R) + else: + self.dfs(node.right, L, R) From 5423bd4762ee318c16ca55ec248341181c106853 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 28 Mar 2019 19:51:20 -0700 Subject: [PATCH 190/344] 946 --- 931 Minimum Falling Path Sum.py | 1 + 946 Validate Stack Sequences.py | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 946 Validate Stack Sequences.py diff --git a/931 Minimum Falling Path Sum.py b/931 Minimum Falling Path Sum.py index 85cad4d..b8aeeef 100644 --- a/931 Minimum Falling Path Sum.py +++ b/931 Minimum Falling Path Sum.py @@ -30,6 +30,7 @@ from typing import List from collections import defaultdict + class Solution: def minFallingPathSum(self, A: List[List[int]]) -> int: """ diff --git a/946 Validate Stack Sequences.py b/946 Validate Stack Sequences.py new file mode 100644 index 0000000..1b1c292 --- /dev/null +++ b/946 Validate Stack Sequences.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +Given two sequences pushed and popped with distinct values, return true if and +only if this could have been the result of a sequence of push and pop operations +on an initially empty stack. + +Example 1: + +Input: pushed = [1,2,3,4,5], popped = [4,5,3,2,1] +Output: true +Explanation: We might do the following sequence: +push(1), push(2), push(3), push(4), pop() -> 4, +push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1 +Example 2: + +Input: pushed = [1,2,3,4,5], popped = [4,3,5,1,2] +Output: false +Explanation: 1 cannot be popped before 2. + + +Note: + +0 <= pushed.length == popped.length <= 1000 +0 <= pushed[i], popped[i] < 1000 +pushed is a permutation of popped. +pushed and popped have distinct values. +""" +from typing import List + + +class Solution: + def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool: + """ + maintain a stack and iterate through pushed + """ + j = 0 + n = len(pushed) + stk = [] + for i in range(n): + stk.append(pushed[i]) + while j < n and stk and stk[-1] == popped[j]: + stk.pop() + j += 1 + + return j == n + + def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool: + """ + maintain a stack + """ + i = 0 + j = 0 + stk = [] + n = len(pushed) + while i < n and j < n: + while i < n and (not stk or stk[-1] != popped[j]): + stk.append(pushed[i]) + i += 1 + + stk.pop() + j += 1 + + while j < n and stk and stk[-1] == popped[j]: + stk.pop() + j += 1 + + return not stk From 9ca61d56b47cfd581b4592c033e3101cd64c85b3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 28 Mar 2019 20:25:15 -0700 Subject: [PATCH 191/344] 945 --- 945 Minimum Increment to Make Array Unique.py | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 945 Minimum Increment to Make Array Unique.py diff --git a/945 Minimum Increment to Make Array Unique.py b/945 Minimum Increment to Make Array Unique.py new file mode 100644 index 0000000..511ceef --- /dev/null +++ b/945 Minimum Increment to Make Array Unique.py @@ -0,0 +1,94 @@ +#!/usr/bin/python3 +""" +Given an array of integers A, a move consists of choosing any A[i], and +incrementing it by 1. + +Return the least number of moves to make every value in A unique. + + +Example 1: + +Input: [1,2,2] +Output: 1 +Explanation: After 1 move, the array could be [1, 2, 3]. +Example 2: + +Input: [3,2,1,2,1,7] +Output: 6 +Explanation: After 6 moves, the array could be [3, 4, 1, 2, 5, 7]. +It can be shown with 5 or less moves that it is impossible for the array to have all unique values. + +Note: + +0 <= A.length <= 40000 +0 <= A[i] < 40000 +""" +from typing import List +from collections import Counter + + +class Solution: + def minIncrementForUnique(self, A: List[int]) -> int: + """ + sort + at least previous + 1 + """ + if not A: + return 0 + + A.sort() + ret = 0 + prev = A[0] + for i in range(1, len(A)): + target = prev + 1 + if A[i] < target: + # change A[i] to target + ret += target - A[i] + prev = target + else: + prev = A[i] + return ret + + +class Solution: + def minIncrementForUnique(self, A: List[int]) -> int: + """ + fill the slot and count + A[i] < 40000 + largest count 3999 + 40000 + """ + counter = Counter(A) + q = [] + ret = 0 + for i in range(40000 * 2): + if counter[i] > 1: + q.extend([i] * (counter[i] - 1)) + elif q and counter[i] == 0: + ret += i - q.pop() + return ret + +class Solution: + def minIncrementForUnique(self, A: List[int]) -> int: + """ + sort, a "brute force" solution of incrementing it repeatedly until it is + not unique. + The brute force can be mathematically calculated + + revert to 0, then increase to A[i-1] + k + """ + ret = 0 + A.sort() + A.append(1 << 31 - 1) # append max + demand = 0 + supply = 0 + for i in range(1, len(A)): + if A[i] == A[i-1]: + demand += 1 + # dup_sum += A[i-1] # error + ret -= A[i-1] # smart + else: + supply = min(demand, A[i] - A[i-1] - 1) + # revert to 0, then increase to A[i-1] + k + ret += (A[i-1] + 1 + A[i-1] + supply) * supply // 2 + demand -= supply + + return ret From 008b49658da0ab1dac9da7ffb21bd0467aee2cd7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 28 Mar 2019 23:24:07 -0700 Subject: [PATCH 192/344] 947 --- ... Stones Removed with Same Row or Column.py | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 947 Most Stones Removed with Same Row or Column.py diff --git a/947 Most Stones Removed with Same Row or Column.py b/947 Most Stones Removed with Same Row or Column.py new file mode 100644 index 0000000..89c555b --- /dev/null +++ b/947 Most Stones Removed with Same Row or Column.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +""" +On a 2D plane, we place stones at some integer coordinate points. Each coordinate point may have at most one stone. + +Now, a move consists of removing a stone that shares a column or row with another stone on the grid. + +What is the largest possible number of moves we can make? + + + +Example 1: + +Input: stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]] +Output: 5 +Example 2: + +Input: stones = [[0,0],[0,2],[1,1],[2,0],[2,2]] +Output: 3 +Example 3: + +Input: stones = [[0,0]] +Output: 0 + + +Note: + +1 <= stones.length <= 1000 +0 <= stones[i][j] < 10000 +""" +from typing import List +from collections import defaultdict + + +class Solution: + def removeStones(self, stones: List[List[int]]) -> int: + """ + convert to graph problem + each component in the graph can be removed to only one node + N - #component + + construct graph O(N^2) + DFS - O(N) + """ + G = defaultdict(list) + n = len(stones) + for i in range(n): + for j in range(i): + if stones[i][0] == stones[j][0] or stones[i][1] == stones[j][1]: + G[i].append(j) + G[j].append(i) + + # dfs + comp_cnt = 0 + visited = [False for _ in range(n)] + for i in range(n): + if not visited[i]: + comp_cnt += 1 + self.dfs(G, i, visited) + + return n - comp_cnt + + def dfs(self, G, i, visited): + visited[i] = True + for nbr in G[i]: + if not visited[nbr]: + self.dfs(G, nbr, visited) + + +if __name__ == "__main__": + assert Solution().removeStones([[0,0],[0,2],[1,1],[2,0],[2,2]]) == 3 From 1673b5bb76ff6a3b36c00651baf2aea834e2cd17 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 28 Mar 2019 23:24:25 -0700 Subject: [PATCH 193/344] 941 --- 941 Valid Mountain Array.py | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 941 Valid Mountain Array.py diff --git a/941 Valid Mountain Array.py b/941 Valid Mountain Array.py new file mode 100644 index 0000000..e99e76d --- /dev/null +++ b/941 Valid Mountain Array.py @@ -0,0 +1,55 @@ +#!/usr/bin/python3 +""" +Given an array A of integers, return true if and only if it is a valid mountain array. + +Recall that A is a mountain array if and only if: + +A.length >= 3 +There exists some i with 0 < i < A.length - 1 such that: +A[0] < A[1] < ... A[i-1] < A[i] +A[i] > A[i+1] > ... > A[B.length - 1] + + +Example 1: + +Input: [2,1] +Output: false +Example 2: + +Input: [3,5,5] +Output: false +Example 3: + +Input: [0,3,2,1] +Output: true + + +Note: + +0 <= A.length <= 10000 +0 <= A[i] <= 10000 +""" +from typing import List + + +class Solution: + def validMountainArray(self, A: List[int]) -> bool: + """ + related to 845 Longest Mountain in Array + + use a flag + """ + incr = 0 # 0 undecided, 1 increasing, 2 decresing + for i in range(1, len(A)): + if A[i] == A[i-1]: + return False + elif A[i] > A[i-1]: + if incr == 2: + return False + incr = 1 + else: + if incr == 0: + return False + incr = 2 + + return incr == 2 From 7d387aae86c3a51db622db27fb595be351c2194e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 30 Mar 2019 19:52:16 -0700 Subject: [PATCH 194/344] 953 --- 953 Verifying an Alien Dictionary.py | 70 ++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 953 Verifying an Alien Dictionary.py diff --git a/953 Verifying an Alien Dictionary.py b/953 Verifying an Alien Dictionary.py new file mode 100644 index 0000000..109d369 --- /dev/null +++ b/953 Verifying an Alien Dictionary.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +""" +In an alien language, surprisingly they also use english lowercase letters, but +possibly in a different order. The order of the alphabet is some permutation of +lowercase letters. + +Given a sequence of words written in the alien language, and the order of the +alphabet, return true if and only if the given words are sorted lexicographicaly +in this alien language. + + + +Example 1: + +Input: words = ["hello","leetcode"], order = "hlabcdefgijkmnopqrstuvwxyz" +Output: true +Explanation: As 'h' comes before 'l' in this language, then the sequence is +sorted. +Example 2: + +Input: words = ["word","world","row"], order = "worldabcefghijkmnpqstuvxyz" +Output: false +Explanation: As 'd' comes after 'l' in this language, then words[0] > words[1], +hence the sequence is unsorted. +Example 3: + +Input: words = ["apple","app"], order = "abcdefghijklmnopqrstuvwxyz" +Output: false +Explanation: The first three characters "app" match, and the second string is +shorter (in size.) According to lexicographical rules "apple" > "app", because 'l' > '∅', where '∅' is defined as the blank character which is less than any other character (More info). + +Note: + +1 <= words.length <= 100 +1 <= words[i].length <= 20 +order.length == 26 +All characters in words[i] and order are english lowercase letters. +""" +from typing import List + + +class Solution: + def isAlienSorted(self, words: List[str], order: str) -> bool: + h = {} + for i, c in enumerate(order): + h[c] = i + + for i in range(1, len(words)): + if self.cmp(words[i], words[i-1], h) == -1: + return False + + return True + + def cmp(self, w1, w2, h): + for c1, c2 in zip(w1, w2): + if h[c1] < h[c2]: + return -1 + elif h[c1] > h[c2]: + return 1 + + if len(w1) == len(w2): + return 0 + elif len(w1) > len(w2): + return 1 + else: + return -1 + + +if __name__ == "__main__": + assert Solution().isAlienSorted(["hello","leetcode"], "hlabcdefgijkmnopqrstuvwxyz") == True From 06012e9fd1c332a9b92771706e30ebf650720e4e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 30 Mar 2019 19:59:16 -0700 Subject: [PATCH 195/344] 951 --- 951 Flip Equivalent Binary Trees.py | 50 +++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 951 Flip Equivalent Binary Trees.py diff --git a/951 Flip Equivalent Binary Trees.py b/951 Flip Equivalent Binary Trees.py new file mode 100644 index 0000000..d704f2d --- /dev/null +++ b/951 Flip Equivalent Binary Trees.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +""" +For a binary tree T, we can define a flip operation as follows: choose any node, +and swap the left and right child subtrees. + +A binary tree X is flip equivalent to a binary tree Y if and only if we can make +X equal to Y after some number of flip operations. + +Write a function that determines whether two binary trees are flip equivalent. +The trees are given by root nodes root1 and root2. + + + +Example 1: + +Input: root1 = [1,2,3,4,5,6,null,null,null,7,8], root2 = [1,3,2,null,6,4,5,null,null,null,null,8,7] +Output: true +Explanation: We flipped at nodes with values 1, 3, and 5. +Flipped Trees Diagram + + +Note: + +Each tree will have at most 100 nodes. +Each value in each tree will be a unique integer in the range [0, 99]. +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def flipEquiv(self, root1: TreeNode, root2: TreeNode) -> bool: + """ + O(N) + """ + if not root1 and not root2: + return True + elif not root1 or not root2: + return False + + if root1.val != root2.val: + return False + + return self.flipEquiv(root1.left, root2.left) and self.flipEquiv(root1.right, root2.right) or \ + self.flipEquiv(root1.left, root2.right) and self.flipEquiv(root1.right, root2.left) From 2cabb06df7f93870f7d83397686db91ba39905e7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 30 Mar 2019 20:25:31 -0700 Subject: [PATCH 196/344] 958 --- 958 Check Completeness of a Binary Tree.py | 61 ++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 958 Check Completeness of a Binary Tree.py diff --git a/958 Check Completeness of a Binary Tree.py b/958 Check Completeness of a Binary Tree.py new file mode 100644 index 0000000..4d6bff1 --- /dev/null +++ b/958 Check Completeness of a Binary Tree.py @@ -0,0 +1,61 @@ +#!/usr/bin/python3 +""" +Given a binary tree, determine if it is a complete binary tree. + +Definition of a complete binary tree from Wikipedia: +In a complete binary tree every level, except possibly the last, is completely +filled, and all nodes in the last level are as far left as possible. It can have +between 1 and 2h nodes inclusive at the last level h. + +Example 1: + +Input: [1,2,3,4,5,6] +Output: true +Explanation: Every level before the last is full (ie. levels with node-values +{1} and {2, 3}), and all nodes in the last level ({4, 5, 6}) are as far left as possible. +Example 2: + +Input: [1,2,3,4,5,null,7] +Output: false +Explanation: The node with value 7 isn't as far left as possible. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.max_depth = -float("inf") + self.expecting_partial = False + + def isCompleteTree(self, root: TreeNode) -> bool: + """ + Do it in one path + left first dfs + record the max depth and expecting partial fill in the last level + """ + return self.dfs(root, 0) + + def dfs(self, node, d): + if not node: + # empty node is the key decision point + if self.max_depth == -float("inf"): # leftmost empty node + self.max_depth = d - 1 + return True + elif self.expecting_partial: + return d == self.max_depth + else: + if d == self.max_depth + 1: + return True + if d == self.max_depth: + self.expecting_partial = True + return True + return False + + return self.dfs(node.left, d + 1) and self.dfs(node.right, d + 1) From 4c1f70227e7f9c1cfdd9a5055873d4bfc8e320cd Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 30 Mar 2019 20:54:15 -0700 Subject: [PATCH 197/344] 954 --- 954 Array of Doubled Pairs.py | 80 +++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 954 Array of Doubled Pairs.py diff --git a/954 Array of Doubled Pairs.py b/954 Array of Doubled Pairs.py new file mode 100644 index 0000000..3031101 --- /dev/null +++ b/954 Array of Doubled Pairs.py @@ -0,0 +1,80 @@ +#!/usr/bin/python3 +""" +Given an array of integers A with even length, return true if and only if it is +possible to reorder it such that A[2 * i + 1] = 2 * A[2 * i] for every +0 <= i < len(A) / 2. + + + +Example 1: + +Input: [3,1,3,6] +Output: false +Example 2: + +Input: [2,1,2,6] +Output: false +Example 3: + +Input: [4,-2,2,-4] +Output: true +Explanation: We can take two groups, [-2,-4] and [2,4] to form [-2,-4,2,4] or +[2,4,-2,-4]. +Example 4: + +Input: [1,2,4,16,8,4] +Output: false + + +Note: + +0 <= A.length <= 30000 +A.length is even +-100000 <= A[i] <= 100000 +""" +from typing import List +from collections import Counter + + +class Solution: + def canReorderDoubled(self, A: List[int]) -> bool: + A.sort(key=abs) + counter = Counter(A) + for a in A: + if counter[a] == 0: + continue + if counter[2*a] == 0: + return False + + counter[a] -= 1 + counter[2*a] -= 1 + + return True + + def canReorderDoubled_positive_negative(self, A: List[int]) -> bool: + """ + sort + counter to form the doubled pairs + """ + A.sort() + counter = Counter(A) + for a in A: + if counter[a] == 0: + continue + counter[a] -= 1 + if a > 0: + target = 2 * a + elif a % 2 != 0: + return False + else: + target = a // 2 + + if counter[target] > 0: + counter[target] -= 1 + else: + return False + + return True + + +if __name__ == "__main__": + assert Solution().canReorderDoubled([4,-2,2,-4]) == True From be8d5d8d6d08695b3a11e71563b85fe21c381529 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 30 Mar 2019 21:46:49 -0700 Subject: [PATCH 198/344] 962 --- 962 Maximum Width Ramp.py | 49 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 962 Maximum Width Ramp.py diff --git a/962 Maximum Width Ramp.py b/962 Maximum Width Ramp.py new file mode 100644 index 0000000..5ca7a57 --- /dev/null +++ b/962 Maximum Width Ramp.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" +Given an array A of integers, a ramp is a tuple (i, j) for which i < j and +A[i] <= A[j]. The width of such a ramp is j - i. + +Find the maximum width of a ramp in A. If one doesn't exist, return 0. + + + +Example 1: + +Input: [6,0,8,2,1,5] +Output: 4 +Explanation: +The maximum width ramp is achieved at (i, j) = (1, 5): A[1] = 0 and A[5] = 5. +Example 2: + +Input: [9,8,1,0,1,9,4,0,4,1] +Output: 7 +Explanation: +The maximum width ramp is achieved at (i, j) = (2, 9): A[2] = 1 and A[9] = 1. + + +Note: + +2 <= A.length <= 50000 +0 <= A[i] <= 50000 +""" +from typing import List + + +class Solution: + def maxWidthRamp(self, A: List[int]) -> int: + """ + Use stack? No, since require the furthest element rather than the closest + Sort the values, keep its index + Iterate the vlaues in increasing order, calcualte j - i + Need to keep the smallest index + """ + ret = -float("inf") + V = [(a, i) for i, a in enumerate(A)] + V.sort() + min_idx = float("inf") + for _, i in V: + # V is sorted, guarantee a' > a + ret = max(ret, i - min_idx) + min_idx = min(min_idx, i) + + return max(ret, 0) From 4dfcfc98fc4a1ea27c8ca1a329bab7f3aff8d7f3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 30 Mar 2019 21:57:29 -0700 Subject: [PATCH 199/344] 950 --- 950 Reveal Cards In Increasing Order.py | 68 +++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 950 Reveal Cards In Increasing Order.py diff --git a/950 Reveal Cards In Increasing Order.py b/950 Reveal Cards In Increasing Order.py new file mode 100644 index 0000000..7f12cac --- /dev/null +++ b/950 Reveal Cards In Increasing Order.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 +""" +In a deck of cards, every card has a unique integer. You can order the deck in +any order you want. + +Initially, all the cards start face down (unrevealed) in one deck. + +Now, you do the following steps repeatedly, until all cards are revealed: + +Take the top card of the deck, reveal it, and take it out of the deck. +If there are still cards in the deck, put the next top card of the deck at the +bottom of the deck. +If there are still unrevealed cards, go back to step 1. Otherwise, stop. +Return an ordering of the deck that would reveal the cards in increasing order. + +The first entry in the answer is considered to be the top of the deck. + + + +Example 1: + +Input: [17,13,11,2,3,5,7] +Output: [2,13,3,11,5,17,7] +Explanation: +We get the deck in the order [17,13,11,2,3,5,7] (this order doesn't matter), and reorder it. +After reordering, the deck starts as [2,13,3,11,5,17,7], where 2 is the top of the deck. +We reveal 2, and move 13 to the bottom. The deck is now [3,11,5,17,7,13]. +We reveal 3, and move 11 to the bottom. The deck is now [5,17,7,13,11]. +We reveal 5, and move 17 to the bottom. The deck is now [7,13,11,17]. +We reveal 7, and move 13 to the bottom. The deck is now [11,17,13]. +We reveal 11, and move 17 to the bottom. The deck is now [13,17]. +We reveal 13, and move 17 to the bottom. The deck is now [17]. +We reveal 17. +Since all the cards revealed are in increasing order, the answer is correct. + + +Note: +1 <= A.length <= 1000 +1 <= A[i] <= 10^6 +A[i] != A[j] for all i != j +""" +from typing import List +from collections import deque + + +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + """ + Sorted is [2, 3, 5, 7, 11, 13, 17] + 17 is the last card, start from the right + Reverse the process + + 17 -> 13 -> 11 + 17 17 + 13 + + Reverse the proess of move top card to the bottom - move the bottom card + to the top + """ + q = deque() + deck.sort() + for i in range(len(deck) - 1, -1, -1): + if q: + tail = q.pop() + q.appendleft(tail) + q.appendleft(deck[i]) + + return list(q) From ea0fb6f5a4a7bb98c41af39a27a7e1bd550f4eb8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 31 Mar 2019 14:07:35 -0700 Subject: [PATCH 200/344] 955 --- 955 Delete Columns to Make Sorted II.py | 77 +++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 955 Delete Columns to Make Sorted II.py diff --git a/955 Delete Columns to Make Sorted II.py b/955 Delete Columns to Make Sorted II.py new file mode 100644 index 0000000..b68163e --- /dev/null +++ b/955 Delete Columns to Make Sorted II.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 +""" +We are given an array A of N lowercase letter strings, all of the same length. + +Now, we may choose any set of deletion indices, and for each string, we delete +all the characters in those indices. + +For example, if we have an array A = ["abcdef","uvwxyz"] and deletion indices +{0, 2, 3}, then the final array after deletions is ["bef","vyz"]. + +Suppose we chose a set of deletion indices D such that after deletions, the +final array has its elements in lexicographic order (A[0] <= A[1] <= A[2] ... +<= A[A.length - 1]). + +Return the minimum possible value of D.length. + + + +Example 1: + +Input: ["ca","bb","ac"] +Output: 1 +Explanation: +After deleting the first column, A = ["a", "b", "c"]. +Now A is in lexicographic order (ie. A[0] <= A[1] <= A[2]). +We require at least 1 deletion since initially A was not in lexicographic order, +so the answer is 1. +Example 2: + +Input: ["xc","yb","za"] +Output: 0 +Explanation: +A is already in lexicographic order, so we don't need to delete anything. +Note that the rows of A are not necessarily in lexicographic order: +ie. it is NOT necessarily true that (A[0][0] <= A[0][1] <= ...) +Example 3: + +Input: ["zyx","wvu","tsr"] +Output: 3 +Explanation: +We have to delete every column. + + +Note: + +1 <= A.length <= 100 +1 <= A[i].length <= 100 +""" +from typing import List + + +class Solution: + def minDeletionSize(self, A: List[str]) -> int: + """ + Greedily delete + Scannign from left to right + Keep a lt array to reprent already sorted pair + lt[i] is true meaning A[i] < A[i+1] + + handle equal case [aa, ab, aa] + """ + m, n = len(A), len(A[0]) + lt = [False for i in range(m)] + deleted = 0 + for j in range(n): + for i in range(m-1): + if lt[i]: + continue + if A[i][j] > A[i+1][j]: + deleted += 1 + break + else: # not deleted + # handle equal case + for i in range(m-1): + lt[i] = lt[i] or A[i][j] < A[i+1][j] + + return deleted From 44dc076ba099f613ac42fbd26cf788786e06d7dd Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 31 Mar 2019 16:25:01 -0700 Subject: [PATCH 201/344] 959 --- 959 Regions Cut By Slashes.py | 154 ++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 959 Regions Cut By Slashes.py diff --git a/959 Regions Cut By Slashes.py b/959 Regions Cut By Slashes.py new file mode 100644 index 0000000..56b6d0f --- /dev/null +++ b/959 Regions Cut By Slashes.py @@ -0,0 +1,154 @@ +#!/usr/bin/python3 +""" +In a N x N grid composed of 1 x 1 squares, each 1 x 1 square consists of a /, \, +or blank space. These characters divide the square into contiguous regions. + +(Note that backslash characters are escaped, so a \ is represented as "\\".) + +Return the number of regions. + + +Example 1: + +Input: +[ + " /", + "/ " +] +Output: 2 +Explanation: The 2x2 grid is as follows: + +Example 2: + +Input: +[ + " /", + " " +] +Output: 1 +Explanation: The 2x2 grid is as follows: + +Example 3: + +Input: +[ + "\\/", + "/\\" +] +Output: 4 +Explanation: (Recall that because \ characters are escaped, "\\/" refers to \/, +and "/\\" refers to /\.) +The 2x2 grid is as follows: + +Example 4: + +Input: +[ + "/\\", + "\\/" +] +Output: 5 +Explanation: (Recall that because \ characters are escaped, "/\\" refers to /\, +and "\\/" refers to \/.) +The 2x2 grid is as follows: + +Example 5: + +Input: +[ + "//", + "/ " +] +Output: 3 +Explanation: The 2x2 grid is as follows: + + + +Note: + +1 <= grid.length == grid[0].length <= 30 +grid[i][j] is either '/', '\', or ' '. +""" +from typing import List + + +class DisjointSet: + def __init__(self): + """ + unbalanced DisjointSet + """ + self.pi = {} + + def union(self, x, y): + pi_x = self.find(x) + pi_y = self.find(y) + self.pi[pi_y] = pi_x + + def find(self, x): + # LHS self.pi[x] + if x not in self.pi: + self.pi[x] = x + if self.pi[x] != x: + self.pi[x] = self.find(self.pi[x]) + return self.pi[x] + +class Solution: + def regionsBySlashes(self, grid: List[str]) -> int: + """ + in 1 x 1 cell + 3 possibilities + ___ + | | + |___| + ___ + | /| + |/__| + ___ + |\ | + |__\| + + 4 regions in the + ___ + |\ /| + |/_\| + """ + m, n = len(grid), len(grid[0]) + ds = DisjointSet() + T, R, B, L = range(4) # top, right, bottom, left + for i in range(m): + for j in range(n): + e = grid[i][j] + if e == "/" or e == " ": + ds.union((i, j, B), (i, j, R)) + ds.union((i, j, T), (i, j, L)) + if e == "\\" or e == " ": # not elif + ds.union((i, j, T), (i, j, R)) + ds.union((i, j, B), (i, j, L)) + # nbr + if i - 1 >= 0: + ds.union((i, j, T), (i-1, j, B)) + if j - 1 >= 0: + ds.union((i, j, L), (i, j-1, R)) + + # unnessary, half closed half open + # if i + 1 < m: + # ds.union((i, j, B), (i+1, j, T)) + # if j + 1 < n: + # ds.union((i, j, R), (i, j+1, L)) + + + return len(set( + ds.find(x) + for x in ds.pi.keys() + )) + + +if __name__ == "__main__": + assert Solution().regionsBySlashes([ + " /", + "/ " + ]) == 2 + assert Solution().regionsBySlashes([ + "//", + "/ " + ]) == 3 From edea470eeb5f3e63e62365edba160a68754209f7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 1 Apr 2019 23:43:06 -0700 Subject: [PATCH 202/344] 965 --- 965 Univalued Binary Tree.py | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 965 Univalued Binary Tree.py diff --git a/965 Univalued Binary Tree.py b/965 Univalued Binary Tree.py new file mode 100644 index 0000000..ad0c84e --- /dev/null +++ b/965 Univalued Binary Tree.py @@ -0,0 +1,45 @@ +#!/usr/bin/python3 +""" +A binary tree is univalued if every node in the tree has the same value. + +Return true if and only if the given tree is univalued. + + + +Example 1: + + +Input: [1,1,1,1,1,null,1] +Output: true +Example 2: + + +Input: [2,2,2,5,2] +Output: false + + +Note: + +The number of nodes in the given tree will be in the range [1, 100]. +Each node's value will be an integer in the range [0, 99]. +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def isUnivalTree(self, root: TreeNode) -> bool: + return self.dfs(root, root.val if root else None) + + def dfs(self, node, val) -> bool: + if not node: + return True + if node.val != val: + return False + + return self.dfs(node.left, val) and self.dfs(node.right, val) From ceaddb68b529291b2cbd5f6dbce36d4391f8bd41 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 3 Apr 2019 22:15:36 -0700 Subject: [PATCH 203/344] 973 --- 973 K Closest Points to Origin.py | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 973 K Closest Points to Origin.py diff --git a/973 K Closest Points to Origin.py b/973 K Closest Points to Origin.py new file mode 100644 index 0000000..a58491d --- /dev/null +++ b/973 K Closest Points to Origin.py @@ -0,0 +1,39 @@ +#!/usr/bin/python3 +""" +We have a list of points on the plane. Find the K closest points to the origin +(0, 0). + +(Here, the distance between two points on a plane is the Euclidean distance.) + +You may return the answer in any order. The answer is guaranteed to be unique +(except for the order that it is in.) + +Example 1: + +Input: points = [[1,3],[-2,2]], K = 1 +Output: [[-2,2]] +Explanation: +The distance between (1, 3) and the origin is sqrt(10). +The distance between (-2, 2) and the origin is sqrt(8). +Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. +We only want the closest K = 1 points from the origin, so the answer is just +[[-2,2]]. +Example 2: + +Input: points = [[3,3],[5,-1],[-2,4]], K = 2 +Output: [[3,3],[-2,4]] +(The answer [[-2,4],[3,3]] would also be accepted.) + +Note: + +1 <= K <= points.length <= 10000 +-10000 < points[i][0] < 10000 +-10000 < points[i][1] < 10000 +""" +from typing import List +import heapq + + +class Solution: + def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]: + return heapq.nsmallest(K, points, key=lambda (a, b): a**2 + b**2)) From b2205b0c320c3dae4ebebde78fc666ff35788758 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 3 Apr 2019 22:39:09 -0700 Subject: [PATCH 204/344] 977 --- 973 K Closest Points to Origin.py | 2 +- 977 Squares of a Sorted Array.py | 43 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 977 Squares of a Sorted Array.py diff --git a/973 K Closest Points to Origin.py b/973 K Closest Points to Origin.py index a58491d..31edddf 100644 --- a/973 K Closest Points to Origin.py +++ b/973 K Closest Points to Origin.py @@ -36,4 +36,4 @@ class Solution: def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]: - return heapq.nsmallest(K, points, key=lambda (a, b): a**2 + b**2)) + return heapq.nsmallest(K, points, key=lambda x: x[0]**2 + x[1]**2) diff --git a/977 Squares of a Sorted Array.py b/977 Squares of a Sorted Array.py new file mode 100644 index 0000000..204bd43 --- /dev/null +++ b/977 Squares of a Sorted Array.py @@ -0,0 +1,43 @@ +#!/usr/bin/python3 +""" +Given an array of integers A sorted in non-decreasing order, return an array of +the squares of each number, also in sorted non-decreasing order. + +Example 1: + +Input: [-4,-1,0,3,10] +Output: [0,1,9,16,100] +Example 2: + +Input: [-7,-3,2,3,11] +Output: [4,9,9,49,121] + + +Note: + +1 <= A.length <= 10000 +-10000 <= A[i] <= 10000 +A is sorted in non-decreasing order. +""" +from typing import List +from collections import deque + + +class Solution: + def sortedSquares(self, A: List[int]) -> List[int]: + """ + started from two ends + """ + n = len(A) + ret = deque() + lo = 0 + hi = n + while lo < hi: + if A[lo] ** 2 < A[hi - 1] ** 2: + ret.appendleft(A[hi - 1] ** 2) + hi -= 1 + else: + ret.appendleft(A[lo] ** 2) + lo += 1 + + return list(ret) From 6bb034464d68443306b50f0a267e20b2224be8b4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 3 Apr 2019 22:51:47 -0700 Subject: [PATCH 205/344] 976 --- 976 Largest Perimeter Triangle.py | 44 +++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 976 Largest Perimeter Triangle.py diff --git a/976 Largest Perimeter Triangle.py b/976 Largest Perimeter Triangle.py new file mode 100644 index 0000000..84c7f64 --- /dev/null +++ b/976 Largest Perimeter Triangle.py @@ -0,0 +1,44 @@ +#!/usr/bin/python3 +""" +Given an array A of positive lengths, return the largest perimeter of a triangle +with non-zero area, formed from 3 of these lengths. + +If it is impossible to form any triangle of non-zero area, return 0. + +Example 1: + +Input: [2,1,2] +Output: 5 +Example 2: + +Input: [1,2,1] +Output: 0 +Example 3: + +Input: [3,2,3,4] +Output: 10 +Example 4: + +Input: [3,6,2,3] +Output: 8 + + +Note: + +3 <= A.length <= 10000 +1 <= A[i] <= 10^6 +""" +from typing import List + + +class Solution: + def largestPerimeter(self, A: List[int]) -> int: + """ + sort and scanning from right + """ + A.sort() + for i in range(len(A) - 3, -1, -1): + if A[i] + A[i+1] > A[i+2]: + return sum(A[i:i+3]) + else: + return 0 From 03d59979a9a863bf4b5108a072ec6a16bf24de52 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 3 Apr 2019 23:56:16 -0700 Subject: [PATCH 206/344] 974 --- 974 Subarray Sums Divisible by K.py | 62 +++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 974 Subarray Sums Divisible by K.py diff --git a/974 Subarray Sums Divisible by K.py b/974 Subarray Sums Divisible by K.py new file mode 100644 index 0000000..23e6e90 --- /dev/null +++ b/974 Subarray Sums Divisible by K.py @@ -0,0 +1,62 @@ +#!/usr/bin/python3 +""" +Given an array A of integers, return the number of (contiguous, non-empty) +subarrays that have a sum divisible by K. + +Example 1: + +Input: A = [4,5,0,-2,-3,1], K = 5 +Output: 7 +Explanation: There are 7 subarrays with a sum divisible by K = 5: +[4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3] + +Note: + +1 <= A.length <= 30000 +-10000 <= A[i] <= 10000 +2 <= K <= 10000 +""" +from typing import List +from collections import defaultdict + + +class Solution: + def subarraysDivByK_2(self, A: List[int], K: int) -> int: + """ + count the prefix sum mod K + nC2 + """ + prefix_sum = 0 + counter = defaultdict(int) + counter[0] = 1 # important trival case + for a in A: + prefix_sum += a + prefix_sum %= K + counter[prefix_sum] += 1 + + ret = 0 + for v in counter.values(): + ret += v * (v-1) // 2 + + return ret + + def subarraysDivByK(self, A: List[int], K: int) -> int: + """ + Prefix sum + O(N^2) + How to optimize? + Mapping to prefix sum to count + Divide: Translate divisible by K into mod. + prefix sum has to be MOD by K. + """ + prefix_sum = 0 + counter = defaultdict(int) + counter[0] = 1 # trival case. !important + ret = 0 + for a in A: + prefix_sum += a + prefix_sum %= K + ret += counter[prefix_sum] # count of previously matching prefix sum + counter[prefix_sum] += 1 + + return ret From 3f20659c24fd59830e378ccb7f5abb392f7c6bf6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 4 Apr 2019 00:14:16 -0700 Subject: [PATCH 207/344] 971 --- ...Binary Tree To Match Preorder Traversal.py | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 971 Flip Binary Tree To Match Preorder Traversal.py diff --git a/971 Flip Binary Tree To Match Preorder Traversal.py b/971 Flip Binary Tree To Match Preorder Traversal.py new file mode 100644 index 0000000..7d2759f --- /dev/null +++ b/971 Flip Binary Tree To Match Preorder Traversal.py @@ -0,0 +1,81 @@ +#!/usr/bin/python3 +""" +Given a binary tree with N nodes, each node has a different value from +{1, ..., N}. + +A node in this binary tree can be flipped by swapping the left child and the +right child of that node. + +Consider the sequence of N values reported by a preorder traversal starting from +the root. Call such a sequence of N values the voyage of the tree. + +(Recall that a preorder traversal of a node means we report the current node's +value, then preorder-traverse the left child, then preorder-traverse the right +child.) + +Our goal is to flip the least number of nodes in the tree so that the voyage of +the tree matches the voyage we are given. + +If we can do so, then return a list of the values of all nodes flipped. You may +return the answer in any order. + +If we cannot do so, then return the list [-1]. + +Example 1: + +Input: root = [1,2], voyage = [2,1] +Output: [-1] +Example 2: + +Input: root = [1,2,3], voyage = [1,3,2] +Output: [1] +Example 3: + +Input: root = [1,2,3], voyage = [1,2,3] +Output: [] + +Note: + +1 <= N <= 100 +""" +from typing import List + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.ret = [] + self.i = 0 # currently scanning index of voyage + + def flipMatchVoyage(self, root: TreeNode, voyage: List[int]) -> List[int]: + """ + match the voyage + Flip the least number of nodes? There is only one answer + """ + self.dfs(root, voyage) + return self.ret + + def dfs(self, node, voyage): + if not node: + return + + if node.val != voyage[self.i]: + self.ret = [-1] + return + + self.i += 1 + if node.left and node.right and node.left.val != voyage[self.i]: + # flip left and right + self.ret.append(node.val) + self.dfs(node.right, voyage) + self.dfs(node.left, voyage) + else: + self.dfs(node.left, voyage) + self.dfs(node.right, voyage) From e140505a2544f78aa267e9749946cd92783138ac Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 4 Apr 2019 22:23:24 -0700 Subject: [PATCH 208/344] 967 --- ...mbers With Same Consecutive Differences.py | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 967 Numbers With Same Consecutive Differences.py diff --git a/967 Numbers With Same Consecutive Differences.py b/967 Numbers With Same Consecutive Differences.py new file mode 100644 index 0000000..9ce7409 --- /dev/null +++ b/967 Numbers With Same Consecutive Differences.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +""" +Return all non-negative integers of length N such that the absolute difference +between every two consecutive digits is K. + +Note that every number in the answer must not have leading zeros except for the +number 0 itself. For example, 01 has one leading zero and is invalid, but 0 is valid. + +You may return the answer in any order. + + + +Example 1: + +Input: N = 3, K = 7 +Output: [181,292,707,818,929] +Explanation: Note that 070 is not a valid number, because it has leading zeroes. +Example 2: + +Input: N = 2, K = 1 +Output: [10,12,21,23,32,34,43,45,54,56,65,67,76,78,87,89,98] + + +Note: + +1 <= N <= 9 +0 <= K <= 9 +""" +from typing import List + + +class Solution: + def __init__(self): + self.cache = {} + + def numsSameConsecDiff(self, N: int, K: int) -> List[int]: + """ + dfs + memoization + """ + ret = [] + for i in range(1, 10): + ret.extend(self.dfs(i, N, K)) + + if N == 1: + ret.append([0]) # special case + + return list( + map(lambda x: int("".join(map(str, x))), ret) + ) + + def dfs(self, start: int, N: int, K: int) -> List[List[int]]: + if (start, N, K) not in self.cache: + ret = [] + if N == 1: + ret = [[start]] + elif N > 1: + if start + K <= 9: + for e in self.dfs(start + K, N - 1, K): + ret.append([start] + e) + if start - K >= 0 and K != 0: # special case + for e in self.dfs(start - K, N - 1, K): + ret.append([start] + e) + + self.cache[start, N, K] = ret + + return self.cache[start, N, K] + + +if __name__ == "__main__": + Solution().numsSameConsecDiff(3, 7) == [181,292,707,818,929] From 00d65bbe673f081464df10052b98169597e1a198 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 4 Apr 2019 23:27:42 -0700 Subject: [PATCH 209/344] 978 --- 978 Longest Turbulent Subarray.py | 66 ++++++++++++++++++++++++++ 979 Distribute Coins in Binary Tree.py | 66 ++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 978 Longest Turbulent Subarray.py create mode 100644 979 Distribute Coins in Binary Tree.py diff --git a/978 Longest Turbulent Subarray.py b/978 Longest Turbulent Subarray.py new file mode 100644 index 0000000..f5b5491 --- /dev/null +++ b/978 Longest Turbulent Subarray.py @@ -0,0 +1,66 @@ +#!/usr/bin/python3 +""" +A subarray A[i], A[i+1], ..., A[j] of A is said to be turbulent if and only if: + +For i <= k < j, A[k] > A[k+1] when k is odd, and A[k] < A[k+1] when k is even; +OR, for i <= k < j, A[k] > A[k+1] when k is even, and A[k] < A[k+1] when k is +odd. +That is, the subarray is turbulent if the comparison sign flips between each +adjacent pair of elements in the subarray. + +Return the length of a maximum size turbulent subarray of A. + +Example 1: + +Input: [9,4,2,10,7,8,8,1,9] +Output: 5 +Explanation: (A[1] > A[2] < A[3] > A[4] < A[5]) +Example 2: + +Input: [4,8,12,16] +Output: 2 +Example 3: + +Input: [100] +Output: 1 + +Note: + +1 <= A.length <= 40000 +0 <= A[i] <= 10^9 +""" +from typing import List + + +class Solution: + def maxTurbulenceSize(self, A: List[int]) -> int: + """ + scan + """ + flag = None # 0: expecting <, 1: expecting > + ret = 1 + cur = 1 + for i in range(len(A)-1): + if A[i] == A[i+1]: + flag = None + cur = 1 + elif A[i] > A[i+1]: + if flag is None or flag == 1: + cur += 1 + ret = max(ret, cur) + else: + cur = 2 + flag = 0 + else: # < + if flag is None or flag == 0: + cur += 1 + ret = max(ret, cur) + else: + cur = 2 + flag = 1 + + return ret + + +if __name__ == "__main__": + assert Solution().maxTurbulenceSize([9,4,2,10,7,8,8,1,9]) == 5 diff --git a/979 Distribute Coins in Binary Tree.py b/979 Distribute Coins in Binary Tree.py new file mode 100644 index 0000000..51e17fa --- /dev/null +++ b/979 Distribute Coins in Binary Tree.py @@ -0,0 +1,66 @@ +#!/usr/bin/python3 +""" +Given the root of a binary tree with N nodes, each node in the tree has node.val coins, and there are N coins total. + +In one move, we may choose two adjacent nodes and move one coin from one node to another. (The move may be from parent to child, or from child to parent.) + +Return the number of moves required to make every node have exactly one coin. + +Example 1: + +Input: [3,0,0] +Output: 2 +Explanation: From the root of the tree, we move one coin to its left child, and + one coin to its right child. +Example 2: + +Input: [0,3,0] +Output: 3 +Explanation: From the left child of the root, we move two coins to the root +[taking two moves]. Then, we move one coin from the root of the tree to the +right child. +Example 3: + +Input: [1,0,2] +Output: 2 +Example 4: + +Input: [1,0,0,null,3] +Output: 4 + +Note: +1<= N <= 100 +0 <= node.val <= N +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.ret = 0 + + def distributeCoins(self, root: TreeNode) -> int: + """ + dfs + """ + self.demand(root) + return self.ret + + def demand(self, node) -> int: + if not node: + return 0 + + demand_l = self.demand(node.left) + demand_r = self.demand(node.right) + demand_m = 1 - node.val + # attribut the move to the node required + demand = demand_l + demand_r + demand_m + self.ret += abs(demand) + return demand From 49071fd70a6b1ee0062b2476e02021b9630fba29 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 11:29:55 -0700 Subject: [PATCH 210/344] 983 --- 983 Minimum Cost For Tickets.py | 125 ++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 983 Minimum Cost For Tickets.py diff --git a/983 Minimum Cost For Tickets.py b/983 Minimum Cost For Tickets.py new file mode 100644 index 0000000..c917f0b --- /dev/null +++ b/983 Minimum Cost For Tickets.py @@ -0,0 +1,125 @@ +#!/usr/bin/python3 +""" +In a country popular for train travel, you have planned some train travelling +one year in advance. The days of the year that you will travel is given as an +array days. Each day is an integer from 1 to 365. + +Train tickets are sold in 3 different ways: + +a 1-day pass is sold for costs[0] dollars; +a 7-day pass is sold for costs[1] dollars; +a 30-day pass is sold for costs[2] dollars. +The passes allow that many days of consecutive travel. For example, if we get +a 7-day pass on day 2, then we can travel for 7 days: day 2, 3, 4, 5, 6, 7, and +8. + +Return the minimum number of dollars you need to travel every day in the given +list of days. + +Example 1: + +Input: days = [1,4,6,7,8,20], costs = [2,7,15] +Output: 11 +Explanation: +For example, here is one way to buy passes that lets you travel your travel plan: +On day 1, you bought a 1-day pass for costs[0] = $2, which covered day 1. +On day 3, you bought a 7-day pass for costs[1] = $7, which covered days 3, 4, ..., 9. +On day 20, you bought a 1-day pass for costs[0] = $2, which covered day 20. +In total you spent $11 and covered all the days of your travel. +Example 2: + +Input: days = [1,2,3,4,5,6,7,8,9,10,30,31], costs = [2,7,15] +Output: 17 +Explanation: +For example, here is one way to buy passes that lets you travel your travel plan: +On day 1, you bought a 30-day pass for costs[2] = $15 which covered days 1, 2, ..., 30. +On day 31, you bought a 1-day pass for costs[0] = $2 which covered day 31. +In total you spent $17 and covered all the days of your travel. + +Note: + +1 <= days.length <= 365 +1 <= days[i] <= 365 +days is in strictly increasing order. +costs.length == 3 +1 <= costs[i] <= 1000 +""" +from typing import List + + +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + """ + Iterate backward. + + Why does iterate backward work? Currrent min depends on the future mins + + Let F[i] be the min cost at day i, covering all trips from i to 365 + F[i] = min(F[i + d] + c for d, c in zip([1, 7, 30], costs)) + If day i is not travel day, then wait until i + k that is a travel day + + O(365) + """ + F = [float("inf") for _ in range(366 + 30)] + for i in range(366, 366 + 30): + F[i] = 0 + + days_set = set(days) + for i in range(365, 0, -1): + if i not in days_set: + F[i] = F[i+1] + else: + F[i] = min( + c + F[i+d] + for d, c in zip([1, 7, 30], costs) + ) + + return F[1] + + def mincostTickets_error(self, days: List[int], costs: List[int]) -> int: + """ + Iterate backward on days + O(30 * |days|) + + Iterate throughout the year is more elegant + Need buffer day + """ + n = len(days) + F = [float("inf") for _ in range(n)] + F[-1] = costs[0] + for i in range(n-2, -1, -1): + for j in range(i+1, n): + delta = days[j] - days[i] + if delta <= 1: + F[i] = min(F[i], costs[0] + F[j]) + if delta <= 7: + F[i] = min(F[i], costs[1] + F[j]) + if delta <= 30: + F[i] = min(F[i], costs[2] + F[j]) + else: + break + return F[0] + + def mincostTickets_error(self, days: List[int], costs: List[int]) -> int: + """ + dp + Let F[i] be the min cost at day i, covering all trips from day 1 to i + For 30-day ticiekt, iterate forward all 30 days? + How to express idle date? Same as previous. + + Why does iterate forward fail? Because future min does not depends on the + current min. Current higher cost may contribtue to future lower cost. + """ + F = [float("inf") for _ in range(365 + 1)] + F[0] = 0 + days_set = set(days) + for i in range(1, 366): + if i not in days_set: + F[i] = F[i-1] + else: + # iterate forward does not work + F[i] = min(F[i], F[i-1] + costs[0]) + + +if __name__ == "__main__": + assert Solution().mincostTickets([1,4,6,7,8,20], [2,7,15]) == 11 From e193818814af4232092075c082768ad7fd278538 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 11:36:53 -0700 Subject: [PATCH 211/344] 985 --- 985 Sum of Even Numbers After Queries.py | 53 ++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 985 Sum of Even Numbers After Queries.py diff --git a/985 Sum of Even Numbers After Queries.py b/985 Sum of Even Numbers After Queries.py new file mode 100644 index 0000000..a7d6072 --- /dev/null +++ b/985 Sum of Even Numbers After Queries.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +""" +We have an array A of integers, and an array queries of queries. + +For the i-th query val = queries[i][0], index = queries[i][1], we add val to +A[index]. Then, the answer to the i-th query is the sum of the even values of +A. + +(Here, the given index = queries[i][1] is a 0-based index, and each query +permanently modifies the array A.) + +Return the answer to all queries. Your answer array should have answer[i] as +the answer to the i-th query. + +Example 1: + +Input: A = [1,2,3,4], queries = [[1,0],[-3,1],[-4,0],[2,3]] +Output: [8,6,2,4] +Explanation: +At the beginning, the array is [1,2,3,4]. +After adding 1 to A[0], the array is [2,2,3,4], and the sum of even values is 2 + 2 + 4 = 8. +After adding -3 to A[1], the array is [2,-1,3,4], and the sum of even values is 2 + 4 = 6. +After adding -4 to A[0], the array is [-2,-1,3,4], and the sum of even values is -2 + 4 = 2. +After adding 2 to A[3], the array is [-2,-1,3,6], and the sum of even values is -2 + 6 = 4. + +Note: + +1 <= A.length <= 10000 +-10000 <= A[i] <= 10000 +1 <= queries.length <= 10000 +-10000 <= queries[i][0] <= 10000 +0 <= queries[i][1] < A.length +""" +from typing import List + + +class Solution: + def sumEvenAfterQueries(self, A: List[int], queries: List[List[int]]) -> List[int]: + """ + maintain a sum + """ + cur_sum = sum(filter(lambda x: x % 2 == 0, A)) + ret = [] + for val, idx in queries: + prev = A[idx] + if prev % 2 == 0: + cur_sum -= prev + A[idx] += val + if A[idx] % 2 == 0: + cur_sum += A[idx] + ret.append(cur_sum) + + return ret From b0e60422daea4dc1116a727b2cc87b174db751dd Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 12:03:13 -0700 Subject: [PATCH 212/344] 986 --- 986 Interval List Intersections.py | 77 ++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 986 Interval List Intersections.py diff --git a/986 Interval List Intersections.py b/986 Interval List Intersections.py new file mode 100644 index 0000000..ec97cfc --- /dev/null +++ b/986 Interval List Intersections.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 +""" +Given two lists of closed intervals, each list of intervals is pairwise disjoint +and in sorted order. + +Return the intersection of these two interval lists. + +(Formally, a closed interval [a, b] (with a <= b) denotes the set of real +numbers x with a <= x <= b. The intersection of two closed intervals is a set +of real numbers that is either empty, or can be represented as a closed interval. +For example, the intersection of [1, 3] and [2, 4] is [2, 3].) + +Example 1: + +Input: A = [[0,2],[5,10],[13,23],[24,25]], B = [[1,5],[8,12],[15,24],[25,26]] +Output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]] +Reminder: The inputs and the desired output are lists of Interval objects, and not arrays or lists. + +Note: +0 <= A.length < 1000 +0 <= B.length < 1000 +0 <= A[i].start, A[i].end, B[i].start, B[i].end < 10^9 +""" +from typing import List + + +# Definition for an interval. +class Interval: + def __init__(self, s=0, e=0): + self.start = s + self.end = e + + +class Solution: + def intervalIntersection(self, A: List[Interval], B: List[Interval]) -> List[Interval]: + """ + merge by checking max starts and min ends + pop by ends + """ + i, j = 0, 0 + m, n = len(A), len(B) + ret = [] + while i < m and j < n: + lo = max(A[i].start, B[j].start) + hi = min(A[i].end, B[j].end) + if lo <= hi: + ret.append(Interval(lo, hi)) + if A[i].end > B[j].end: + j += 1 + else: + i += 1 + + return ret + + def intervalIntersection_complex(self, A: List[Interval], B: List[Interval]) -> List[Interval]: + """ + like merge + """ + ret = [] + i = 0 + j = 0 + m, n = len(A), len(B) + while i < m and j < n: + a = A[i] + b = B[j] + if b.start <= a.end <= b.end: + ret.append(Interval(max(a.start, b.start), a.end)) + i += 1 + elif a.start <= b.end <= a.end: + ret.append(Interval(max(a.start, b.start), b.end)) + j += 1 + else: + if a.end < b.start: + i += 1 + else: + j += 1 + return ret From cdd82925e82716330bf6d63f8314a23442183d41 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 12:46:59 -0700 Subject: [PATCH 213/344] 993 --- 993 Cousins in Binary Tree.py | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 993 Cousins in Binary Tree.py diff --git a/993 Cousins in Binary Tree.py b/993 Cousins in Binary Tree.py new file mode 100644 index 0000000..437641a --- /dev/null +++ b/993 Cousins in Binary Tree.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 +""" +In a binary tree, the root node is at depth 0, and children of each depth k node +are at depth k+1. + +Two nodes of a binary tree are cousins if they have the same depth, but have +different parents. + +We are given the root of a binary tree with unique values, and the values x and +y of two different nodes in the tree. + +Return true if and only if the nodes corresponding to the values x and y are +cousins. + +Example 1: + +Input: root = [1,2,3,4], x = 4, y = 3 +Output: false +Example 2: + +Input: root = [1,2,3,null,4,null,5], x = 5, y = 4 +Output: true +Example 3: + +Input: root = [1,2,3,null,4], x = 2, y = 3 +Output: false + +Note: + +The number of nodes in the tree will be between 2 and 100. +Each node has a unique integer value from 1 to 100. +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.pi = [] + self.depths = [] + + def isCousins(self, root: TreeNode, x: int, y: int) -> bool: + """ + need to know parent and depth + """ + self.dfs(None, root, x, 0) + self.dfs(None, root, y, 0) + if len(self.pi) != 2: + return False + return self.pi[0] != self.pi[1] and self.depths[0] == self.depths[1] + + + def dfs(self, pi, node, x, depth): + if not node: + return + + if node.val == x: + self.pi.append(pi) + self.depths.append(depth) + return + + self.dfs(node, node.left, x, depth + 1) + self.dfs(node, node.right, x, depth + 1) From 075dc1b3a55caff9fc50b989b6dc8a28f7bfaff8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 12:59:06 -0700 Subject: [PATCH 214/344] 989 --- 989 Add to Array-Form of Integer.py | 51 +++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 989 Add to Array-Form of Integer.py diff --git a/989 Add to Array-Form of Integer.py b/989 Add to Array-Form of Integer.py new file mode 100644 index 0000000..2cb8fbb --- /dev/null +++ b/989 Add to Array-Form of Integer.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 +""" +For a non-negative integer X, the array-form of X is an array of its digits in +left to right order. For example, if X = 1231, then the array form is [1,2,3,1]. + +Given the array-form A of a non-negative integer X, return the array-form of the integer X+K. + + + +Example 1: + +Input: A = [1,2,0,0], K = 34 +Output: [1,2,3,4] +Explanation: 1200 + 34 = 1234 +Example 2: + +Input: A = [2,7,4], K = 181 +Output: [4,5,5] +Explanation: 274 + 181 = 455 +Example 3: + +Input: A = [2,1,5], K = 806 +Output: [1,0,2,1] +Explanation: 215 + 806 = 1021 +Example 4: + +Input: A = [9,9,9,9,9,9,9,9,9,9], K = 1 +Output: [1,0,0,0,0,0,0,0,0,0,0] +Explanation: 9999999999 + 1 = 10000000000 +""" +from typing import List +from collections import deque + + +class Solution: + def addToArrayForm(self, A: List[int], K: int) -> List[int]: + """ + carry + """ + carry = K + for i in range(len(A)-1, -1, -1): + A[i] += carry + carry = A[i] // 10 + A[i] %= 10 + + head = deque() + while carry: + head.appendleft(carry % 10) + carry //= 10 + + return list(head) + A From d1660be5d1ad81fe0c27953caf1bd4da944e9278 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 13:21:04 -0700 Subject: [PATCH 215/344] 988 --- 988 Smallest String Starting From Leaf.py | 74 +++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 988 Smallest String Starting From Leaf.py diff --git a/988 Smallest String Starting From Leaf.py b/988 Smallest String Starting From Leaf.py new file mode 100644 index 0000000..9312f34 --- /dev/null +++ b/988 Smallest String Starting From Leaf.py @@ -0,0 +1,74 @@ +#!/usr/bin/python3 +""" +Given the root of a binary tree, each node has a value from 0 to 25 representing +the letters 'a' to 'z': a value of 0 represents 'a', a value of 1 represents +'b', and so on. + +Find the lexicographically smallest string that starts at a leaf of this tree +and ends at the root. + +(As a reminder, any shorter prefix of a string is lexicographically smaller: for +example, "ab" is lexicographically smaller than "aba". A leaf of a node is a +node that has no children.) + +Example 1: + +Input: [0,1,2,3,4,3,4] +Output: "dba" +Example 2: + +Input: [25,1,3,1,3,0,2] +Output: "adz" +Example 3: + +Input: [2,2,1,null,1,0,null,0] +Output: "abc" + +Note: +The number of nodes in the given tree will be between 1 and 8500. +Each node in the tree will have a value between 0 and 25. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +from typing import Tuple +from collections import deque + + +class Solution: + def __init__(self): + self.mn: Tuple[int] = None + + def smallestFromLeaf(self, root: TreeNode) -> str: + """ + dfs + """ + self.dfs(root, deque()) + if not self.mn: + return "" + return "".join( + chr(e + ord("a")) + for e in self.mn + ) + + def dfs(self, node, cur_deque): + if not node: + return + + cur_deque.appendleft(node.val) + if not node.left and not node.right: + t = tuple(cur_deque) + if not self.mn or t < self.mn: + self.mn = t + else: + self.dfs(node.left, cur_deque) + self.dfs(node.right, cur_deque) + # need to pop at the end + cur_deque.popleft() From 55a6afbcef0ca50a5c93ed2d6c8addc4beecf125 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 14:11:07 -0700 Subject: [PATCH 216/344] 994 --- 994 Rotting Oranges.py | 73 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 994 Rotting Oranges.py diff --git a/994 Rotting Oranges.py b/994 Rotting Oranges.py new file mode 100644 index 0000000..222f1aa --- /dev/null +++ b/994 Rotting Oranges.py @@ -0,0 +1,73 @@ +#!/usr/bin/python3 +""" +In a given grid, each cell can have one of three values: + +the value 0 representing an empty cell; +the value 1 representing a fresh orange; +the value 2 representing a rotten orange. +Every minute, any fresh orange that is adjacent (4-directionally) to a rotten +orange becomes rotten. + +Return the minimum number of minutes that must elapse until no cell has a fresh +orange. If this is impossible, return -1 instead. + +Example 1: + +Input: [[2,1,1],[1,1,0],[0,1,1]] +Output: 4 +Example 2: + +Input: [[2,1,1],[0,1,1],[1,0,1]] +Output: -1 +Explanation: The orange in the bottom left corner (row 2, column 0) is never +rotten, because rotting only happens 4-directionally. +Example 3: + +Input: [[0,2]] +Output: 0 +Explanation: Since there are already no fresh oranges at minute 0, the answer +is just 0. + +Note: +1 <= grid.length <= 10 +1 <= grid[0].length <= 10 +grid[i][j] is only 0, 1, or 2. +""" +from typing import List + + +dirs = ((0, -1), (0, 1), (-1, 0), (1, 0)) + + +class Solution: + def orangesRotting(self, grid: List[List[int]]) -> int: + """ + maintain a q for the newly rotten + """ + m, n = len(grid), len(grid[0]) + q = [] + for i in range(m): + for j in range(n): + if grid[i][j] == 2: + q.append((i, j)) + + t = -1 + while q: + t += 1 + cur_q = [] + for i, j in q: + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < m and 0 <= J < n and grid[I][J] == 1: + grid[I][J] = 2 + cur_q.append((I, J)) + q = cur_q + + has_fresh = any( + grid[i][j] == 1 + for i in range(m) + for j in range(n) + ) + + return max(0, t) if not has_fresh else -1 From e81bded15d4bf6852a2f70f5331e9a963d0f8147 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 14:27:24 -0700 Subject: [PATCH 217/344] 987 --- ...rtical Order Traversal of a Binary Tree.py | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 987 Vertical Order Traversal of a Binary Tree.py diff --git a/987 Vertical Order Traversal of a Binary Tree.py b/987 Vertical Order Traversal of a Binary Tree.py new file mode 100644 index 0000000..4771b93 --- /dev/null +++ b/987 Vertical Order Traversal of a Binary Tree.py @@ -0,0 +1,76 @@ +#!/usr/bin/python3 +""" +Given a binary tree, return the vertical order traversal of its nodes values. + +For each node at position (X, Y), its left and right children respectively will +be at positions (X-1, Y-1) and (X+1, Y-1). + +Running a vertical line from X = -infinity to X = +infinity, whenever the +vertical line touches some nodes, we report the values of the nodes in order +from top to bottom (decreasing Y coordinates). + +If two nodes have the same position, then the value of the node that is +reported first is the value that is smaller. + +Return an list of non-empty reports in order of X coordinate. Every report +will have a list of values of nodes. + +Example 1: + +Input: [3,9,20,null,null,15,7] +Output: [[9],[3,15],[20],[7]] +Explanation: +Without loss of generality, we can assume the root node is at position (0, 0): +Then, the node with value 9 occurs at position (-1, -1); +The nodes with values 3 and 15 occur at positions (0, 0) and (0, -2); +The node with value 20 occurs at position (1, -1); +The node with value 7 occurs at position (2, -2). +Example 2: + +Input: [1,2,3,4,5,6,7] +Output: [[4],[2],[1,5,6],[3],[7]] +Explanation: +The node with value 5 and the node with value 6 have the same position according to the given scheme. +However, in the report "[1,5,6]", the node value of 5 comes first since 5 is smaller than 6. + + +Note: + +The tree will have between 1 and 1000 nodes. +Each node's value will be between 0 and 1000. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +from collections import defaultdict + + +class Solution: + def __init__(self): + self.mp = defaultdict(list) # element (-Y, val) # from left to right, top to bottom + + def verticalTraversal(self, root: TreeNode) -> List[List[int]]: + self.dfs(root, 0, 0) + ret = [] + mn = min(self.mp) + mx = max(self.mp) + for i in range(mn, mx+1): + ret.append([ + val + for _, val in sorted(self.mp[i]) + ]) + return ret + + def dfs(self, node, x, y): + if not node: + return + self.mp[x].append((-y, node.val)) + self.dfs(node.left, x-1, y-1) + self.dfs(node.right, x+1, y-1) From a04319dfe455db7869cea018e3693dd3b8861a5c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 14:37:39 -0700 Subject: [PATCH 218/344] 997 --- 997 Find the Town Judge.py | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 997 Find the Town Judge.py diff --git a/997 Find the Town Judge.py b/997 Find the Town Judge.py new file mode 100644 index 0000000..0476350 --- /dev/null +++ b/997 Find the Town Judge.py @@ -0,0 +1,64 @@ +#!/usr/bin/python3 +""" +In a town, there are N people labelled from 1 to N. There is a rumor that one +of these people is secretly the town judge. + +If the town judge exists, then: + +The town judge trusts nobody. +Everybody (except for the town judge) trusts the town judge. +There is exactly one person that satisfies properties 1 and 2. +You are given trust, an array of pairs trust[i] = [a, b] representing that the +person labelled a trusts the person labelled b. + +If the town judge exists and can be identified, return the label of the town +judge. Otherwise, return -1. + +Example 1: + +Input: N = 2, trust = [[1,2]] +Output: 2 +Example 2: + +Input: N = 3, trust = [[1,3],[2,3]] +Output: 3 +Example 3: + +Input: N = 3, trust = [[1,3],[2,3],[3,1]] +Output: -1 +Example 4: + +Input: N = 3, trust = [[1,2],[2,3]] +Output: -1 +Example 5: + +Input: N = 4, trust = [[1,3],[1,4],[2,3],[2,4],[4,3]] +Output: 3 + + +Note: + +1 <= N <= 1000 +trust.length <= 10000 +trust[i] are all different +trust[i][0] != trust[i][1] +1 <= trust[i][0], trust[i][1] <= N +""" +from typing import List +from collections import defaultdict + + +class Solution: + def findJudge(self, N: int, trust: List[List[int]]) -> int: + """ + like the find the celebrity + """ + ingress = defaultdict(set) + egress =defaultdict(set) + for p, q in trust: + egress[p].add(q) + ingress[q].add(p) + for i in range(1, N+1): + if len(egress[i]) == 0 and len(ingress[i]) == N - 1: + return i + return -1 From 83969bf1fe77250a33d037bf0e9178b75e3be2ac Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 14:47:50 -0700 Subject: [PATCH 219/344] 990 --- 990 Satisfiability of Equality Equations.py | 83 +++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 990 Satisfiability of Equality Equations.py diff --git a/990 Satisfiability of Equality Equations.py b/990 Satisfiability of Equality Equations.py new file mode 100644 index 0000000..d2242fb --- /dev/null +++ b/990 Satisfiability of Equality Equations.py @@ -0,0 +1,83 @@ +#!/usr/bin/python3 +""" +Given an array equations of strings that represent relationships between +variables, each string equations[i] has length 4 and takes one of two different +forms: "a==b" or "a!=b". Here, a and b are lowercase letters (not necessarily +different) that represent one-letter variable names. + +Return true if and only if it is possible to assign integers to variable names +so as to satisfy all the given equations. + + + +Example 1: + +Input: ["a==b","b!=a"] +Output: false +Explanation: If we assign say, a = 1 and b = 1, then the first equation is +satisfied, but not the second. There is no way to assign the variables to +satisfy both equations. +Example 2: + +Input: ["b==a","a==b"] +Output: true +Explanation: We could assign a = 1 and b = 1 to satisfy both equations. +Example 3: + +Input: ["a==b","b==c","a==c"] +Output: true +Example 4: + +Input: ["a==b","b!=c","c==a"] +Output: false +Example 5: + +Input: ["c==c","b==d","x!=z"] +Output: true + +Note: + +1 <= equations.length <= 500 +equations[i].length == 4 +equations[i][0] and equations[i][3] are lowercase letters +equations[i][1] is either '=' or '!' +equations[i][2] is '=' +""" +from typing import List + + +class DisjointSet: + def __init__(self): + self.pi = {} + + def union(self, x, y): + self.pi[self.find(x)] = self.find(y) + + def find(self, x): + if x not in self.pi: + self.pi[x] = x + elif self.pi[x] != x: + self.pi[x] = self.find(self.pi[x]) + return self.pi[x] + +class Solution: + def equationsPossible(self, equations: List[str]) -> bool: + """ + union find + """ + ds = DisjointSet() + neqs = [] # list of neq + for e in equations: + a = e[0] + b = e[-1] + sign = e[1:-1] + if sign == "==": + ds.union(a, b) + else: + neqs.append((a, b)) + + for a, b in neqs: + if ds.find(a) == ds.find(b): + return False + + return True From 22b679e0124c22e93ddc974bad93c24d45fb4fef Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 15:24:59 -0700 Subject: [PATCH 220/344] 991 --- 991 Broken Calculator.py | 94 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 991 Broken Calculator.py diff --git a/991 Broken Calculator.py b/991 Broken Calculator.py new file mode 100644 index 0000000..8f1bc50 --- /dev/null +++ b/991 Broken Calculator.py @@ -0,0 +1,94 @@ +#!/usr/bin/python3 +""" +On a broken calculator that has a number showing on its display, we can perform +two operations: + +Double: Multiply the number on the display by 2, or; +Decrement: Subtract 1 from the number on the display. +Initially, the calculator is displaying the number X. + +Return the minimum number of operations needed to display the number Y. + + + +Example 1: + +Input: X = 2, Y = 3 +Output: 2 +Explanation: Use double operation and then decrement operation {2 -> 4 -> 3}. +Example 2: + +Input: X = 5, Y = 8 +Output: 2 +Explanation: Use decrement and then double {5 -> 4 -> 8}. +Example 3: + +Input: X = 3, Y = 10 +Output: 3 +Explanation: Use double, decrement and double {3 -> 6 -> 5 -> 10}. +Example 4: + +Input: X = 1024, Y = 1 +Output: 1023 +Explanation: Use decrement operations 1023 times. + + +Note: + +1 <= X <= 10^9 +1 <= Y <= 10^9 +""" + + +class Solution: + def brokenCalc(self, X: int, Y: int) -> int: + """ + greedy + work backward + + If Y is odd, we can do only Y = Y + 1 + If Y is even, if we plus 1 to Y, then Y is odd, we need to plus another 1. + And because (Y + 1 + 1) / 2 = (Y / 2) + 1, 3 operations are more than 2. + We always choose Y / 2 if Y is even. + """ + t = 0 + while Y > X: + if Y % 2 == 0: + Y //= 2 + else: + Y += 1 + t += 1 + + return t + X - Y + + def brokenCalc_TLE(self, X: int, Y: int) -> int: + """ + BFS + """ + q = [X] + t = 0 + has_larger = False + while q: + cur_q = [] + for e in q: + if e == Y: + return t + + cur = e * 2 + if cur >= 1: + if cur > Y and not has_larger: + has_larger = True + cur_q.append(cur) + elif cur <= Y: + cur_q.append(cur) + + cur = e - 1 + if cur >= 1: + cur_q.append(cur) + q = cur_q + t += 1 + + raise + + +if __name__ == "__main__": + assert Solution().brokenCalc(2, 3) == 2 From 55c61566b1d9638eda18cdf3f34597a452626f9f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 16:52:40 -0700 Subject: [PATCH 221/344] 981 --- 981 Time Based Key-Value Store.py | 89 +++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 981 Time Based Key-Value Store.py diff --git a/981 Time Based Key-Value Store.py b/981 Time Based Key-Value Store.py new file mode 100644 index 0000000..28e8297 --- /dev/null +++ b/981 Time Based Key-Value Store.py @@ -0,0 +1,89 @@ +#!/usr/bin/python3 +""" +Create a timebased key-value store class TimeMap, that supports two operations. + +1. set(string key, string value, int timestamp) + +Stores the key and value, along with the given timestamp. +2. get(string key, int timestamp) + +Returns a value such that set(key, value, timestamp_prev) was called previously, +with timestamp_prev <= timestamp. +If there are multiple such values, it returns the one with the largest +timestamp_prev. +If there are no values, it returns the empty string (""). + + +Example 1: + +Input: inputs = ["TimeMap","set","get","get","set","get","get"], inputs = + [[],["foo","bar",1],["foo",1],["foo",3],["foo","bar2",4],["foo",4],["foo",5]] +Output: [null,null,"bar","bar",null,"bar2","bar2"] +Explanation: +TimeMap kv; +kv.set("foo", "bar", 1); // store the key "foo" and value "bar" along with +timestamp = 1 +kv.get("foo", 1); // output "bar" +kv.get("foo", 3); // output "bar" since there is no value corresponding to foo +at timestamp 3 and timestamp 2, then the only value is at timestamp 1 ie "bar" +kv.set("foo", "bar2", 4); +kv.get("foo", 4); // output "bar2" +kv.get("foo", 5); //output "bar2" + +Example 2: + +Input: inputs = ["TimeMap","set","set","get","get","get","get","get"], inputs = +[[],["love","high",10],["love","low",20],["love",5],["love",10],["love",15], +["love",20],["love",25]] +Output: [null,null,null,"","high","high","low","low"] + + +Note: + +All key/value strings are lowercase. +All key/value strings have length in the range [1, 100] +The timestamps for all TimeMap.set operations are strictly increasing. +1 <= timestamp <= 10^7 +TimeMap.set and TimeMap.get functions will be called a total of 120000 times +combined) per test case. +""" +import bisect +from collections import defaultdict + + +class TimeMap: + + def __init__(self): + """ + Initialize your data structure here. + Looks like Cassandra + Heap? Largest timestamp_prev <= timestamp + BST + Bineary search + """ + self.m = defaultdict(list) + + def set(self, key: str, value: str, timestamp: int) -> None: + n = (timestamp, value) + bisect.insort(self.m[key], n) + + def get(self, key: str, timestamp: int) -> str: + if key not in self.m: + return "" + + # find the largest v, s.t. v <= t + lst = self.m[key] + i = bisect.bisect(lst, (timestamp, "")) + if i < len(lst) and lst[i][0] == timestamp: + return lst[i][1] + i -= 1 + if i >= 0: + return lst[i][1] + + return "" + + +# Your TimeMap object will be instantiated and called as such: +# obj = TimeMap() +# obj.set(key,value,timestamp) +# param_2 = obj.get(key,timestamp) From 53a5521f1bcee7b400e4897b1d63fb02d4daeab4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 18:22:33 -0700 Subject: [PATCH 222/344] 968 --- 968 Binary Tree Cameras.py | 107 +++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 968 Binary Tree Cameras.py diff --git a/968 Binary Tree Cameras.py b/968 Binary Tree Cameras.py new file mode 100644 index 0000000..31c40d9 --- /dev/null +++ b/968 Binary Tree Cameras.py @@ -0,0 +1,107 @@ +#!/usr/bin/python3 +""" +Given a binary tree, we install cameras on the nodes of the tree. + +Each camera at a node can monitor its parent, itself, and its immediate children. + +Calculate the minimum number of cameras needed to monitor all nodes of the tree. + + + +Example 1: + + +Input: [0,0,null,0,0] +Output: 1 +Explanation: One camera is enough to monitor all nodes if placed as shown. +Example 2: + + +Input: [0,0,null,0,null,0,null,null,0] +Output: 2 +Explanation: At least two cameras are needed to monitor all nodes of the tree. +The above image shows one of the valid configurations of camera placement. + +Note: + +The number of nodes in the given tree will be in the range [1, 1000]. +Every node has value 0. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.covered = {None} + self.cnt = 0 + + def minCameraCover(self, root: TreeNode) -> int: + """ + Greedy? + Bottom up, cover leaf's parent is strictly better than cover leaf + """ + self.dfs(root, None) + if root not in self.covered: + self.covered.add(root) + self.cnt += 1 + + return self.cnt + + + def dfs(self, node, pi): + """ + post order + rely on the parents to cover it + """ + if not node: + return + + self.dfs(node.left, node) + self.dfs(node.right, node) + if node.left not in self.covered or node.right not in self.covered: + self.cnt += 1 + self.covered.add(node.left) + self.covered.add(node.right) + self.covered.add(node) + self.covered.add(pi) + + +class SolutionErrror: + def __init__(self): + self.covered = set() + + def minCameraCover(self, root: TreeNode) -> int: + """ + Greedy? + Top-down, no good. + Bottom up, cover leaf's parent is strictly better than cover leaf + """ + dummy = TreeNode(0) + dummy.left = root + self.dfs(root, dummy) + self.covered.discard(dummy) # swallow KeyError + return len(self.covered) + + def dfs(self, node, pi): + """ + post order + """ + if not node: + return + + self.dfs(node.left, node) + self.dfs(node.right, node) + # post oder + if ( + (not node.left or node.left in self.covered) and + (not node.right or node.right in self.covered) + ): + self.covered.add(pi) + return From c063f5dbc66ac7b9d521c28e0a45cbcc2a6f89a2 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 18:22:40 -0700 Subject: [PATCH 223/344] 942 --- 942 DI String Match.py | 70 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 942 DI String Match.py diff --git a/942 DI String Match.py b/942 DI String Match.py new file mode 100644 index 0000000..d188437 --- /dev/null +++ b/942 DI String Match.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +""" +Given a string S that only contains "I" (increase) or "D" (decrease), let N = +S.length. + +Return any permutation A of [0, 1, ..., N] such that for all i = 0, ..., N-1: + +If S[i] == "I", then A[i] < A[i+1] +If S[i] == "D", then A[i] > A[i+1] + + +Example 1: + +Input: "IDID" +Output: [0,4,1,3,2] +Example 2: + +Input: "III" +Output: [0,1,2,3] +Example 3: + +Input: "DDI" +Output: [3,2,0,1] + + +Note: + +1 <= S.length <= 10000 +S only contains characters "I" or "D". +""" +from typing import List + + +class Solution: + def diStringMatch(self, S: str) -> List[int]: + """ + Looking at prev rather than cur + If "I", then put smallest as prev. Increase from the min + If "D", then put the largest as prev. Decrese from the max + """ + mini, maxa = 0, len(S) + ret = [] + for c in S: + if c == "I": + ret.append(mini) + mini += 1 + else: # "D" + ret.append(maxa) + maxa -= 1 + + ret.append(mini) + return ret + + def diStringMatchErrror(self, S: str) -> List[int]: + """ + start with 0, then add the min up to 0 + + errror since cannot repeat + """ + ret = [0] + for c in S: + if c == "I": + ret.append(ret[-1] + 1) + else: + ret.append(ret[-1] -1) + mn = min(ret) + return [ + e - mn + for e in ret + ] From 0a7fc7c371fda56a1c1887f5929cf5b842afaf20 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 20:49:59 -0700 Subject: [PATCH 224/344] 643 --- 643 Maximum Average Subarray I.py | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 643 Maximum Average Subarray I.py diff --git a/643 Maximum Average Subarray I.py b/643 Maximum Average Subarray I.py new file mode 100644 index 0000000..969aafc --- /dev/null +++ b/643 Maximum Average Subarray I.py @@ -0,0 +1,36 @@ +#!/usr/bin/python3 +""" +Given an array consisting of n integers, find the contiguous subarray of given +length k that has the maximum average value. And you need to output the maximum +average value. + +Example 1: + +Input: [1,12,-5,-6,50,3], k = 4 +Output: 12.75 +Explanation: Maximum average is (12-5-6+50)/4 = 51/4 = 12.75 + + +Note: + +1 <= k <= n <= 30,000. +Elements of the given array will be in the range [-10,000, 10,000]. +""" +from typing import List + + +class Solution: + def findMaxAverage(self, nums: List[int], k: int) -> float: + """ + two pointers + """ + cur_sum = sum(nums[:k]) + maxa = cur_sum + i = k + while i < len(nums): + cur_sum += nums[i] + cur_sum -= nums[i-k] + maxa = max(maxa, cur_sum) + i += 1 + + return maxa / k From 24bd82d799bf61dcd814b558f42262484b4b5561 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 21:10:11 -0700 Subject: [PATCH 225/344] 799 --- 799 Champagne Tower.py | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 799 Champagne Tower.py diff --git a/799 Champagne Tower.py b/799 Champagne Tower.py new file mode 100644 index 0000000..692fb86 --- /dev/null +++ b/799 Champagne Tower.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +We stack glasses in a pyramid, where the first row has 1 glass, the second row +has 2 glasses, and so on until the 100th row. Each glass holds one cup (250ml) +of champagne. + +Then, some champagne is poured in the first glass at the top. When the top most +glass is full, any excess liquid poured will fall equally to the glass +immediately to the left and right of it. When those glasses become full, any +excess champagne will fall equally to the left and right of those glasses, and +so on. (A glass at the bottom row has it's excess champagne fall on the floor.) + +For example, after one cup of champagne is poured, the top most glass is full. +After two cups of champagne are poured, the two glasses on the second row are +half full. After three cups of champagne are poured, those two cups become full +- there are 3 full glasses total now. After four cups of champagne are poured, +the third row has the middle glass half full, and the two outside glasses are a +quarter full, as pictured below. + + + +Now after pouring some non-negative integer cups of champagne, return how full +the j-th glass in the i-th row is (both i and j are 0 indexed.) + + + +Example 1: +Input: poured = 1, query_glass = 1, query_row = 1 +Output: 0.0 +Explanation: We poured 1 cup of champange to the top glass of the tower +(which is indexed as (0, 0)). There will be no excess liquid so all the glasses +under the top glass will remain empty. + +Example 2: +Input: poured = 2, query_glass = 1, query_row = 1 +Output: 0.5 +Explanation: We poured 2 cups of champange to the top glass of the tower +(which is indexed as (0, 0)). There is one cup of excess liquid. The glass +indexed as (1, 0) and the glass indexed as (1, 1) will share the excess liquid +equally, and each will get half cup of champange. + + +Note: + +poured will be in the range of [0, 10 ^ 9]. +query_glass and query_row will be in the range of [0, 99]. +""" +from collections import defaultdict + + +class Solution: + def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: + """ + Simulation + Instead of keeping track of how much champagne should end up in a + glass, keep track of the total amount of champagne that flows through a + glass. + """ + G = defaultdict(lambda: defaultdict(int)) + G[0][0] = poured + for i in range(query_row): + for j in range(i+1): # i + 1 glasses at row i + excess = max(0, G[i][j] - 1) + G[i+1][j] += excess / 2 + G[i+1][j+1] += excess / 2 + + return min(1, G[query_row][query_glass]) From dbadddfba53a9b023258ef8e69eb91cccb54d5f6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 5 Apr 2019 21:33:55 -0700 Subject: [PATCH 226/344] 821 --- 821 Shortest Distance to a Character.py | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 821 Shortest Distance to a Character.py diff --git a/821 Shortest Distance to a Character.py b/821 Shortest Distance to a Character.py new file mode 100644 index 0000000..27f3cb5 --- /dev/null +++ b/821 Shortest Distance to a Character.py @@ -0,0 +1,40 @@ +#!/usr/bin/python3 +""" +Given a string S and a character C, return an array of integers representing the +shortest distance from the character C in the string. + +Example 1: + +Input: S = "loveleetcode", C = 'e' +Output: [3, 2, 1, 0, 1, 0, 0, 1, 2, 2, 1, 0] + + +Note: + +S string length is in [1, 10000]. +C is a single character, and guaranteed to be in string S. +All letters in S and C are lowercase. +""" +from typing import List + + +class Solution: + def shortestToChar(self, S: str, C: str) -> List[int]: + """ + get the sorted indexes of C + """ + idx = [ + i + for i in range(len(S)) + if S[i] == C + ] + idx = [-float("inf")] + idx + [float("inf")] + ret = [] + i = 0 + for j in range(len(S)): + while not idx[i] <= j < idx[i+1]: + i += 1 + + ret.append(min(j - idx[i], idx[i+1] - j)) + + return ret From a67274d640b5336d4757a4d771b0b3b6e2987092 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 6 Apr 2019 20:50:48 -0700 Subject: [PATCH 227/344] 784 --- 784 Letter Case Permutation.py | 59 ++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 784 Letter Case Permutation.py diff --git a/784 Letter Case Permutation.py b/784 Letter Case Permutation.py new file mode 100644 index 0000000..1b5374f --- /dev/null +++ b/784 Letter Case Permutation.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 +""" +Given a string S, we can transform every letter individually to be lowercase or +uppercase to create another string. Return a list of all possible strings we +could create. + +Examples: +Input: S = "a1b2" +Output: ["a1b2", "a1B2", "A1b2", "A1B2"] + +Input: S = "3z4" +Output: ["3z4", "3Z4"] + +Input: S = "12345" +Output: ["12345"] +Note: + +S will be a string with length between 1 and 12. +S will consist only of letters or digits. +""" +from typing import List + + +class Solution: + def __init__(self): + self.ret = [] + + def letterCasePermutation(self, S: str) -> List[str]: + """ + dfs + """ + # S_lst = S.split() # error + S_lst = list(S) + self.dfs([], S_lst, 0) + return [ + "".join(e) + for e in self.ret + ] + + def dfs(self, lst, S_lst, i): + if len(lst) == len(S_lst): + self.ret.append(list(lst)) + return + + if S_lst[i].isdigit(): + lst.append(S_lst[i]) + self.dfs(lst, S_lst, i + 1) + lst.pop() + else: + lst.append(S_lst[i].lower()) + self.dfs(lst, S_lst, i + 1) + lst.pop() + lst.append(S_lst[i].upper()) + self.dfs(lst, S_lst, i + 1) + lst.pop() + + +if __name__ == "__main__": + assert Solution().letterCasePermutation("a1b2") == ['a1b2', 'a1B2', 'A1b2', 'A1B2'] From b299a9ae7feec328c5ad4eea8e630a2193ba37dc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 6 Apr 2019 21:40:47 -0700 Subject: [PATCH 228/344] 849 --- 849 Maximize Distance to Closest Person.py | 90 ++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 849 Maximize Distance to Closest Person.py diff --git a/849 Maximize Distance to Closest Person.py b/849 Maximize Distance to Closest Person.py new file mode 100644 index 0000000..a538ed4 --- /dev/null +++ b/849 Maximize Distance to Closest Person.py @@ -0,0 +1,90 @@ +#!/usr/bin/python3 +""" +In a row of seats, 1 represents a person sitting in that seat, and 0 represents +that the seat is empty. + +There is at least one empty seat, and at least one person sitting. + +Alex wants to sit in the seat such that the distance between him and the closest +person to him is maximized. + +Return that maximum distance to closest person. + +Example 1: + +Input: [1,0,0,0,1,0,1] +Output: 2 +Explanation: +If Alex sits in the second open seat (seats[2]), then the closest person has +distance 2. +If Alex sits in any other open seat, the closest person has distance 1. +Thus, the maximum distance to the closest person is 2. +Example 2: + +Input: [1,0,0,0] +Output: 3 +Explanation: +If Alex sits in the last seat, the closest person is 3 seats away. +This is the maximum distance possible, so the answer is 3. +Note: + +1 <= seats.length <= 20000 +seats contains only 0s or 1s, at least one 0, and at least one 1. +""" +from typing import List + + +class Solution: + def maxDistToClosest(self, seats: List[int]) -> int: + """ + DP from left and right - next array + Let L[i] be the distant to the left 1 at A[i] + Let R[i] ... + """ + n = len(seats) + L = [float("inf") for _ in range(n)] + R = [float("inf") for _ in range(n)] + for i in range(n): + if seats[i] == 1: + L[i] = 0 + elif i - 1 >= 0: + L[i] = L[i-1] + 1 + for i in range(n-1, -1 , -1): + if seats[i] == 1: + R[i] = 0 + elif i + 1 < n: + R[i] = R[i+1] + 1 + + return max( + min(L[i], R[i]) + for i in range(n) + ) + + def maxDistToClosest2(self, seats: List[int]) -> int: + """ + maintain a sorrted index array + """ + idxes = [] + for i, e in enumerate(seats): + if e == 1: + idxes.append(i) + + ret = [-float("inf"), 0] + n = len(seats) + # two ends + for i, j in zip((0, n-1), (0, -1)): + dist = abs(i - idxes[j]) + if dist > ret[0]: + ret = [dist, i] + + for j in range(len(idxes) - 1): + i = (idxes[j] + idxes[j+1]) // 2 + dist = min(abs(i - idxes[j]), abs(i - idxes[j+1])) + if dist > ret[0]: + ret = [dist, i] + + return ret[0] + + +if __name__ == "__main__": + assert Solution().maxDistToClosest([1,0,0,0,1,0,1]) == 2 From aa60f41aa7eb1f324049725f3e3feb6940ccd878 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 6 Apr 2019 22:19:11 -0700 Subject: [PATCH 229/344] 838 --- 838 Push Dominoes.py | 87 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 838 Push Dominoes.py diff --git a/838 Push Dominoes.py b/838 Push Dominoes.py new file mode 100644 index 0000000..f49adf6 --- /dev/null +++ b/838 Push Dominoes.py @@ -0,0 +1,87 @@ +#!/usr/bin/python3 +""" +There are N dominoes in a line, and we place each domino vertically upright. + +In the beginning, we simultaneously push some of the dominoes either to the left +or to the right. + + + +After each second, each domino that is falling to the left pushes the adjacent +domino on the left. + +Similarly, the dominoes falling to the right push their adjacent dominoes +standing on the right. + +When a vertical domino has dominoes falling on it from both sides, it stays +still due to the balance of the forces. + +For the purposes of this question, we will consider that a falling domino +expends no additional force to a falling or already fallen domino. + +Given a string "S" representing the initial state. S[i] = 'L', if the i-th +domino has been pushed to the left; S[i] = 'R', if the i-th domino has been pushed to the right; S[i] = '.', if the i-th domino has not been pushed. + +Return a string representing the final state. + +Example 1: + +Input: ".L.R...LR..L.." +Output: "LL.RR.LLRRLL.." +Example 2: + +Input: "RR.L" +Output: "RR.L" +Explanation: The first domino expends no additional force on the second domino. +Note: + +0 <= N <= 10^5 +String dominoes contains only 'L', 'R' and '.' +""" + + +class Solution: + def pushDominoes(self, dominoes: str) -> str: + """ + DP L & R from both ends + Let L[i] be the distance to the "L" from the right + + we will consider that a falling domino expends no additional force + """ + n = len(dominoes) + L = [float("inf") for i in range(n)] + R = [float("inf") for i in range(n)] + for i in range(n-1, -1, -1): + if dominoes[i] == "L": + L[i] = 0 + elif dominoes[i] == "R": + L[i] = float("inf") + elif i + 1 < n: + L[i] = L[i+1] + 1 + + for i in range(n): + if dominoes[i] == "R": + R[i] = 0 + elif dominoes[i] == "L": + R[i] = float("inf") + elif i - 1 >= 0: + R[i] = R[i-1] + 1 + + ret = [] + for i in range(n): + d = min(R[i], L[i]) + if d == float("inf"): + cur = "." + elif R[i] == L[i]: + cur = "." + elif d == R[i]: + cur = "R" + else: + cur = "L" + ret.append(cur) + + return "".join(ret) + + +if __name__ == "__main__": + assert Solution().pushDominoes(".L.R...LR..L..") == "LL.RR.LLRRLL.." From ba4e1d096628164e79f648a206a89f7817a8f883 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 7 Apr 2019 10:21:37 -0700 Subject: [PATCH 230/344] 826 --- 826 Most Profit Assigning Work.py | 50 +++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 826 Most Profit Assigning Work.py diff --git a/826 Most Profit Assigning Work.py b/826 Most Profit Assigning Work.py new file mode 100644 index 0000000..042886a --- /dev/null +++ b/826 Most Profit Assigning Work.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +""" +We have jobs: difficulty[i] is the difficulty of the ith job, and profit[i] is +the profit of the ith job. + +Now we have some workers. worker[i] is the ability of the ith worker, which +means that this worker can only complete a job with difficulty at most worker[i]. + +Every worker can be assigned at most one job, but one job can be completed +multiple times. + +For example, if 3 people attempt the same job that pays $1, then the total +profit will be $3. If a worker cannot complete any job, his profit is $0. + +What is the most profit we can make? + +Example 1: + +Input: difficulty = [2,4,6,8,10], profit = [10,20,30,40,50], worker = [4,5,6,7] +Output: 100 +Explanation: Workers are assigned jobs of difficulty [4,4,6,6] and they get +profit of [20,20,30,30] seperately. +Notes: + +1 <= difficulty.length = profit.length <= 10000 +1 <= worker.length <= 10000 +difficulty[i], profit[i], worker[i] are in range [1, 10^5] +""" +from typing import List + + +class Solution: + def maxProfitAssignment(self, difficulty: List[int], profit: List[int], worker: List[int]) -> int: + """ + Greedy? Sort by profit + """ + tasks = list(sorted(zip(profit, difficulty))) + worker.sort() + i = len(tasks) - 1 + j = len(worker) - 1 + ret = 0 + while i >= 0 and j >= 0: + pro, diff = tasks[i] + if worker[j] >= diff: + ret += pro + j -= 1 + else: + i -= 1 + + return ret From f6bddd1c94ce66c1a775ea44903687fa1e28f473 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 7 Apr 2019 16:02:48 -0700 Subject: [PATCH 231/344] 837 --- 837 New 21 Game.py | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 837 New 21 Game.py diff --git a/837 New 21 Game.py b/837 New 21 Game.py new file mode 100644 index 0000000..fbff914 --- /dev/null +++ b/837 New 21 Game.py @@ -0,0 +1,91 @@ +#!/usr/bin/python3 +""" +Alice plays the following game, loosely based on the card game "21". + +Alice starts with 0 points, and draws numbers while she has less than K points. +During each draw, she gains an integer number of points randomly from the range +[1, W], where W is an integer. Each draw is independent and the outcomes have +equal probabilities. + +Alice stops drawing numbers when she gets K or more points. What is the +probability that she has N or less points? + +Example 1: + +Input: N = 10, K = 1, W = 10 +Output: 1.00000 +Explanation: Alice gets a single card, then stops. +Example 2: + +Input: N = 6, K = 1, W = 10 +Output: 0.60000 +Explanation: Alice gets a single card, then stops. +In 6 out of W = 10 possibilities, she is at or below N = 6 points. +Example 3: + +Input: N = 21, K = 17, W = 10 +Output: 0.73278 +Note: + +0 <= K <= N <= 10000 +1 <= W <= 10000 +Answers will be accepted as correct if they are within 10^-5 of the correct +answer. +The judging time limit has been reduced for this question. +""" + + +class Solution: + def new21Game(self, N: int, K: int, W: int) -> float: + """ + F[i]: probability of get points i + F[i] = F[j] * (1 / W) for every i - j <= W + => O(N*W) + F[i] = sum(last W dp values) * (1 / W) + To get cur_sum, where cur_sum = sum(last W dp values), we can maintain a + sliding window with size at most K. + => O(N) + """ + if K == 0: + return 1 + + F = [0 for _ in range(N+1)] + F[0] = 1 + cur_sum = F[0] + ret = 0 + for i in range(1, N+1): + F[i] = cur_sum * (1/W) + if i >= K: + ret += F[i] + # stop + else: + cur_sum += F[i] + + if i - W >= 0: + cur_sum -= F[i - W] + + return ret + + def new21Game_error(self, N: int, K: int, W: int) -> float: + """ + DP + Let F[i] be the probability of reaching point i + + O(N^2) + """ + F = [0 for _ in range(K+W+1)] + F[0] = 1 + for i in range(1, K+W+1): + for j in range(W, 0, -1): + if i - j >= K: + break + if i - j >= 0: + F[i] += F[i-j] * 1 / W + + ret = sum(F[1:N+1]) # error + print(F, ret) + return ret + + +if __name__ == "__main__": + assert Solution().new21Game(6, 1, 10) == 0.6 From 9ce84e0b0bbed866aa9e207223c97a50e76c227c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 7 Apr 2019 17:09:24 -0700 Subject: [PATCH 232/344] 861 --- 861 Score After Flipping Matrix.py | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 861 Score After Flipping Matrix.py diff --git a/861 Score After Flipping Matrix.py b/861 Score After Flipping Matrix.py new file mode 100644 index 0000000..55a30c9 --- /dev/null +++ b/861 Score After Flipping Matrix.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 +""" +We have a two dimensional matrix A where each value is 0 or 1. + +A move consists of choosing any row or column, and toggling each value in that +row or column: changing all 0s to 1s, and all 1s to 0s. + +After making any number of moves, every row of this matrix is interpreted as a +binary number, and the score of the matrix is the sum of these numbers. + +Return the highest possible score. + +Example 1: + +Input: [[0,0,1,1],[1,0,1,0],[1,1,0,0]] +Output: 39 +Explanation: +Toggled to [[1,1,1,1],[1,0,0,1],[1,1,1,1]]. +0b1111 + 0b1001 + 0b1111 = 15 + 9 + 15 = 39 + +Note: + +1 <= A.length <= 20 +1 <= A[0].length <= 20 +A[i][j] is 0 or 1. +""" +from typing import List + + +class Solution: + def matrixScore(self, A: List[List[int]]) -> int: + """ + MSB > sum of remaining digit + => Toggle rows to set MSB to 1 + Then we cannot toggle row anymore + Toggle the col if #0's < #1's + """ + m, n = len(A), len(A[0]) + ret = 0 + ret += (1 << (n-1)) * m # all rows with MSB being 1 + for j in range(1, n): + cnt = 0 + for i in range(m): + if A[i][j] == A[i][0]: + cnt += 1 # number of 1's + + # toggle + cnt = max(cnt, m-cnt) + ret += (1 << (n-1-j)) * cnt + + return ret From 60e60e460796b911774b50055aa4bb7a281a0a07 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 8 Apr 2019 21:48:55 -0700 Subject: [PATCH 233/344] 855 --- 855 Exam Room.py | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 855 Exam Room.py diff --git a/855 Exam Room.py b/855 Exam Room.py new file mode 100644 index 0000000..49ddc49 --- /dev/null +++ b/855 Exam Room.py @@ -0,0 +1,87 @@ +#!/usr/bin/python3 +""" +In an exam room, there are N seats in a single row, numbered 0, 1, 2, ..., N-1. + +When a student enters the room, they must sit in the seat that maximizes the +distance to the closest person. If there are multiple such seats, they sit in +the seat with the lowest number. (Also, if no one is in the room, then the +student sits at seat number 0.) + +Return a class ExamRoom(int N) that exposes two functions: ExamRoom.seat() +returning an int representing what seat the student sat in, and +ExamRoom.leave(int p) representing that the student in seat number p now leaves +the room. It is guaranteed that any calls to ExamRoom.leave(p) have a student +sitting in seat p. + +Example 1: + +Input: ["ExamRoom","seat","seat","seat","seat","leave","seat"], +[[10],[],[],[],[],[4],[]] +Output: [null,0,9,4,2,null,5] +Explanation: +ExamRoom(10) -> null +seat() -> 0, no one is in the room, then the student sits at seat number 0. +seat() -> 9, the student sits at the last seat number 9. +seat() -> 4, the student sits at the last seat number 4. +seat() -> 2, the student sits at the last seat number 2. +leave(4) -> null +seat() -> 5, the student sits at the last seat number 5. +​​​​​​​ +Note: + +1 <= N <= 10^9 +ExamRoom.seat() and ExamRoom.leave() will be called at most 10^4 times across +all test cases. +Calls to ExamRoom.leave(p) are guaranteed to have a student currently sitting in +seat number p. +""" +import bisect + + +class ExamRoom: + def __init__(self, N: int): + """ + Maintain a sorted array of index. BST + BST -> bisect sort + O(N) per query + """ + self.N = N + self.idxes = [] # sorted arry of taken idx + + def seat(self) -> int: + """ + similar to 849 + """ + if not self.idxes: + ret_idx = 0 + else: + max_dist, ret_idx = 0, 0 + # begin + dist = self.idxes[0] - 0 + if dist > max_dist: + max_dist = dist + ret_idx = 0 + # middle + for j in range(len(self.idxes)-1): + i = (self.idxes[j] + self.idxes[j+1]) // 2 + dist = min(abs(self.idxes[j] - i), abs(self.idxes[j+1] - i)) + if dist > max_dist: + max_dist = dist + ret_idx = i + # end + dist = self.N-1 - self.idxes[-1] + if dist > max_dist: + max_dist = dist + ret_idx = self.N-1 + + bisect.insort(self.idxes, ret_idx) + return ret_idx + + def leave(self, p: int) -> None: + self.idxes.remove(p) + + +# Your ExamRoom object will be instantiated and called as such: +# obj = ExamRoom(N) +# param_1 = obj.seat() +# obj.leave(p) From 16904b8147380c66fd2a29d9e4444ebfe5298131 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 8 Apr 2019 22:02:29 -0700 Subject: [PATCH 234/344] 872 --- 872 Leaf-Similar Trees.py | 57 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 872 Leaf-Similar Trees.py diff --git a/872 Leaf-Similar Trees.py b/872 Leaf-Similar Trees.py new file mode 100644 index 0000000..ef4888a --- /dev/null +++ b/872 Leaf-Similar Trees.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +""" +Consider all the leaves of a binary tree. From left to right order, the values +of those leaves form a leaf value sequence. + +For example, in the given tree above, the leaf value sequence is (6, 7, 4, 9, +8). + +Two binary trees are considered leaf-similar if their leaf value sequence is the +same. + +Return true if and only if the two given trees with head nodes root1 and root2 +are leaf-similar. + +Note: + +Both of the given trees will have between 1 and 100 nodes. +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def leafSimilar(self, root1: TreeNode, root2: TreeNode) -> bool: + """ + brute force, get all the leaf and then compare + to save space, use generator + O(lg n) space for the stack + """ + itr1 = self.dfs(root1) + itr2 = self.dfs(root2) + while True: + a = next(itr1, None) + b = next(itr2, None) + if a != b: + return False + if a is None and b is None: + break + return True + + def dfs(self, node): + stk = [node] + # pre-order + while stk: + cur = stk.pop() + if not cur: + continue + if not cur.left and not cur.right: + yield cur.val + else: + stk.append(cur.right) + stk.append(cur.left) From 30bd826b0dd14eceadf43d0459c4b3dbdf1d4d6d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 10 Apr 2019 11:10:49 -0700 Subject: [PATCH 235/344] 886 --- 886 Possible Bipartition.py | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 886 Possible Bipartition.py diff --git a/886 Possible Bipartition.py b/886 Possible Bipartition.py new file mode 100644 index 0000000..70873bf --- /dev/null +++ b/886 Possible Bipartition.py @@ -0,0 +1,72 @@ +#!/usr/bin/python3 +""" +Given a set of N people (numbered 1, 2, ..., N), we would like to split +everyone into two groups of any size. + +Each person may dislike some other people, and they should not go into the same +group. + +Formally, if dislikes[i] = [a, b], it means it is not allowed to put the people +numbered a and b into the same group. + +Return true if and only if it is possible to split everyone into two groups in +this way. + +Example 1: + +Input: N = 4, dislikes = [[1,2],[1,3],[2,4]] +Output: true +Explanation: group1 [1,4], group2 [2,3] +Example 2: + +Input: N = 3, dislikes = [[1,2],[1,3],[2,3]] +Output: false +Example 3: + +Input: N = 5, dislikes = [[1,2],[2,3],[3,4],[4,5],[1,5]] +Output: false + +Note: + +1 <= N <= 2000 +0 <= dislikes.length <= 10000 +1 <= dislikes[i][j] <= N +dislikes[i][0] < dislikes[i][1] +There does not exist i != j for which dislikes[i] == dislikes[j]. +""" +from typing import List +from collections import defaultdict + + +class Solution: + def possibleBipartition(self, N: int, dislikes: List[List[int]]) -> bool: + """ + If given likes, then we can use union-find. But this is dislikes. + Two bipartition, A, B. For each dislike, do a dfs on A, B. + O(N * M) + + DFS + coloring do a dfs all on nodes O(N) + O(M) + """ + G = defaultdict(list) + for u, v in dislikes: + G[u].append(v) + G[v].append(u) + + visited = {} # 0 color red, 1 color blue + for u in range(1, N+1): + if u not in visited: + if not self.dfs(u, G, visited, 0): + return False + return True + + def dfs(self, u, G, visited, color): + visited[u] = color + for nbr in G[u]: + if nbr in visited: + if visited[nbr] == color: + return False + else: + if not self.dfs(nbr, G, visited, color ^ 1): + return False + + return True From 2164af3730bef8e389812a2806d65713a0ce33ad Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 10 Apr 2019 11:53:57 -0700 Subject: [PATCH 236/344] 888 --- 888 Fair Candy Swap.py | 82 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 888 Fair Candy Swap.py diff --git a/888 Fair Candy Swap.py b/888 Fair Candy Swap.py new file mode 100644 index 0000000..d17a687 --- /dev/null +++ b/888 Fair Candy Swap.py @@ -0,0 +1,82 @@ +#!/usr/bin/python3 +""" +Alice and Bob have candy bars of different sizes: A[i] is the size of the i-th +bar of candy that Alice has, and B[j] is the size of the j-th bar of candy that +Bob has. + +Since they are friends, they would like to exchange one candy bar each so that +after the exchange, they both have the same total amount of candy. (The total +amount of candy a person has is the sum of the sizes of candy bars they have.) + +Return an integer array ans where ans[0] is the size of the candy bar that Alice +must exchange, and ans[1] is the size of the candy bar that Bob must exchange. + +If there are multiple answers, you may return any one of them. It is guaranteed +an answer exists. + +Example 1: + +Input: A = [1,1], B = [2,2] +Output: [1,2] +Example 2: + +Input: A = [1,2], B = [2,3] +Output: [1,2] +Example 3: + +Input: A = [2], B = [1,3] +Output: [2,3] +Example 4: + +Input: A = [1,2,5], B = [2,4] +Output: [5,4] + +Note: + +1 <= A.length <= 10000 +1 <= B.length <= 10000 +1 <= A[i] <= 100000 +1 <= B[i] <= 100000 +It is guaranteed that Alice and Bob have different total amounts of candy. +It is guaranteed there exists an answer. +""" +from typing import List +import bisect + + +class Solution: + def fairCandySwap(self, A: List[int], B: List[int]) -> List[int]: + """ + It is a search problem. Use set as search. + """ + sum_A = sum(A) + sum_B = sum(B) + diff = (sum_B - sum_A) // 2 # it can be negative or positive + set_B = set(B) + for a in A: + if a + diff in set_B: + return [a, a + diff] + + raise + + def fairCandySwap_complex(self, A: List[int], B: List[int]) -> List[int]: + """ + sum, to figure out the target O(N) + exchange one + exchange is (sum - target) + constant + it is a search problem + """ + sum_A = sum(A) + sum_B = sum(B) + if sum_A > sum_B: + return self.fairCandySwap(B, A)[::-1] + + A.sort() + B.sort() + diff = (sum_B - sum_A) // 2 + for a in A: + i = bisect.bisect_left(B, a + diff) + if i < len(B) and B[i] == a + diff: + return [a, a + diff] + + raise From bbcf05d3e20c081c69b72ce773d7ec3988b9416d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 10 Apr 2019 22:15:56 -0700 Subject: [PATCH 237/344] 897 --- 897 Increasing Order Search Tree.py | 77 +++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 897 Increasing Order Search Tree.py diff --git a/897 Increasing Order Search Tree.py b/897 Increasing Order Search Tree.py new file mode 100644 index 0000000..aa58cad --- /dev/null +++ b/897 Increasing Order Search Tree.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 +""" +Given a tree, rearrange the tree in in-order so that the leftmost node in the +tree is now the root of the tree, and every node has no left child and only 1 +right child. + +Example 1: +Input: [5,3,6,2,4,null,8,1,null,null,null,7,9] + + 5 + / \ + 3 6 + / \ \ + 2 4 8 + / / \ +1 7 9 + +Output: [1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9] + + 1 + \ + 2 + \ + 3 + \ + 4 + \ + 5 + \ + 6 + \ + 7 + \ + 8 + \ + 9 +Note: + +The number of nodes in the given tree will be between 1 and 100. +Each node will have a unique integer value from 0 to 1000. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.prev = None + self.root = None + + def increasingBST(self, root: TreeNode) -> TreeNode: + """ + keep a previous index + in-order is easy + """ + self.dfs(root) + return self.root + + def dfs(self, node): + if not node: + return + + self.dfs(node.left) + if not self.prev: + self.root = node + else: + self.prev.right = node + node.left = None # need test case to test it + + self.prev = node + self.dfs(node.right) From 818e499f0668fc1871bd75d939770b44200d895b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 10 Apr 2019 22:31:03 -0700 Subject: [PATCH 238/344] 900 --- 900 RLE Iterator.py | 72 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 900 RLE Iterator.py diff --git a/900 RLE Iterator.py b/900 RLE Iterator.py new file mode 100644 index 0000000..ef4d780 --- /dev/null +++ b/900 RLE Iterator.py @@ -0,0 +1,72 @@ +#!/usr/bin/python3 +""" +Write an iterator that iterates through a run-length encoded sequence. + +The iterator is initialized by RLEIterator(int[] A), where A is a run-length +encoding of some sequence. More specifically, for all even i, A[i] tells us the +number of times that the non-negative integer value A[i+1] is repeated in the +sequence. + +The iterator supports one function: next(int n), which exhausts the next n +elements (n >= 1) and returns the last element exhausted in this way. If there +is no element left to exhaust, next returns -1 instead. + +For example, we start with A = [3,8,0,9,2,5], which is a run-length encoding of +the sequence [8,8,8,5,5]. This is because the sequence can be read as "three +eights, zero nines, two fives". + +Example 1: + +Input: ["RLEIterator","next","next","next","next"], [[[3,8,0,9,2,5]],[2],[1], +[1],[2]] +Output: [null,8,8,5,-1] +Explanation: +RLEIterator is initialized with RLEIterator([3,8,0,9,2,5]). +This maps to the sequence [8,8,8,5,5]. +RLEIterator.next is then called 4 times: + +.next(2) exhausts 2 terms of the sequence, returning 8. The remaining sequence +is now [8, 5, 5]. + +.next(1) exhausts 1 term of the sequence, returning 8. The remaining sequence +is now [5, 5]. + +.next(1) exhausts 1 term of the sequence, returning 5. The remaining sequence +is now [5]. + +.next(2) exhausts 2 terms, returning -1. This is because the first term +exhausted was 5, +but the second term did not exist. Since the last term exhausted does not +exist, we return -1. + +Note: + +0 <= A.length <= 1000 +A.length is an even integer. +0 <= A[i] <= 10^9 +There are at most 1000 calls to RLEIterator.next(int n) per test case. +Each call to RLEIterator.next(int n) will have 1 <= n <= 10^9. +""" +from typing import List + + +class RLEIterator: + def __init__(self, A: List[int]): + """ + counter + """ + self.cur_i = 0 + self.cur_used = 0 + self.A = A + + def next(self, n: int) -> int: + run = self.cur_used + n + while self.cur_i < len(self.A) and run > self.A[self.cur_i]: + run -= self.A[self.cur_i] + self.cur_i += 2 + + if self.cur_i >= len(self.A): + return -1 + + self.cur_used = run + return self.A[self.cur_i + 1] From 5e4e1a35949984660e7c16e20239d3be5b716769 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 10 Apr 2019 23:04:53 -0700 Subject: [PATCH 239/344] 914 --- 914 X of a Kind in a Deck of Cards.py | 70 +++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 914 X of a Kind in a Deck of Cards.py diff --git a/914 X of a Kind in a Deck of Cards.py b/914 X of a Kind in a Deck of Cards.py new file mode 100644 index 0000000..8bc578e --- /dev/null +++ b/914 X of a Kind in a Deck of Cards.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 +""" +In a deck of cards, each card has an integer written on it. + +Return true if and only if you can choose X >= 2 such that it is possible to split the entire deck into 1 or more groups of cards, where: + +Each group has exactly X cards. +All the cards in each group have the same integer. + + +Example 1: + +Input: [1,2,3,4,4,3,2,1] +Output: true +Explanation: Possible partition [1,1],[2,2],[3,3],[4,4] +Example 2: + +Input: [1,1,1,2,2,2,3,3] +Output: false +Explanation: No possible partition. +Example 3: + +Input: [1] +Output: false +Explanation: No possible partition. +Example 4: + +Input: [1,1] +Output: true +Explanation: Possible partition [1,1] +Example 5: + +Input: [1,1,2,2,2,2] +Output: true +Explanation: Possible partition [1,1],[2,2],[2,2] + +Note: + +1 <= deck.length <= 10000 +0 <= deck[i] < 10000 +""" +from typing import List +from collections import Counter + + +class Solution: + def hasGroupsSizeX(self, deck: List[int]) -> bool: + """ + gcd of all > 2 + """ + counter = Counter(deck) + gcd = None + for v in counter.values(): + if gcd is None: + gcd = v + gcd = self.gcd(gcd, v) + if gcd == 1: + return False + + return True + + def gcd(self, a, b): + """ + a = k * b + r + gcd(a, b) = gcd(b, r) + """ + while b: + a, b = b, a % b + + return a From 0d02ab22533f582084262e2e6df24bf858fb7017 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 11 Apr 2019 22:18:09 -0700 Subject: [PATCH 240/344] 911 --- 911 Online Election.py | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 911 Online Election.py diff --git a/911 Online Election.py b/911 Online Election.py new file mode 100644 index 0000000..ba8202d --- /dev/null +++ b/911 Online Election.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +In an election, the i-th vote was cast for persons[i] at time times[i]. + +Now, we would like to implement the following query function: +TopVotedCandidate.q(int t) will return the number of the person that was leading +the election at time t. + +Votes cast at time t will count towards our query. In the case of a tie, the +most recent vote (among tied candidates) wins. + +Example 1: + +Input: ["TopVotedCandidate","q","q","q","q","q","q"], [[[0,1,1,0,0,1,0], +[0,5,10,15,20,25,30]],[3],[12],[25],[15],[24],[8]] +Output: [null,0,1,1,0,0,1] +Explanation: +At time 3, the votes are [0], and 0 is leading. +At time 12, the votes are [0,1,1], and 1 is leading. +At time 25, the votes are [0,1,1,0,0,1], and 1 is leading (as ties go to the +most recent vote.) +This continues for 3 more queries at time 15, 24, and 8. + +Note: + +1 <= persons.length = times.length <= 5000 +0 <= persons[i] <= persons.length +times is a strictly increasing array with all elements in [0, 10^9]. +TopVotedCandidate.q is called at most 10000 times per test case. +TopVotedCandidate.q(int t) is always called with t >= times[0]. +""" +from typing import List +from collections import defaultdict +import bisect + + +class TopVotedCandidate: + def __init__(self, persons: List[int], times: List[int]): + """ + Running top vote + Need to maintain list + + but time is too large to enumerate. Cannot have direct access, then + query is binary search + """ + self.maxes = [] # [(t, i)] at time t + counter = defaultdict(int) + tp = sorted(zip(times, persons)) + for t, p in tp: + counter[p] += 1 + if not self.maxes or counter[self.maxes[-1][1]] <= counter[p]: + self.maxes.append((t, p)) + + def q(self, t: int) -> int: + i = bisect.bisect(self.maxes, (t, 0)) + # equal + if i < len(self.maxes) and self.maxes[i][0] == t: + return self.maxes[i][1] + + # smaller + i -= 1 + return self.maxes[i][1] + + +# Your TopVotedCandidate object will be instantiated and called as such: +# obj = TopVotedCandidate(persons, times) +# param_1 = obj.q(t) From 889a0e626c650d2ceb56b3750c408f2e7a14324d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 11 Apr 2019 23:55:37 -0700 Subject: [PATCH 241/344] 910 --- 910 Smallest Range II.py | 73 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 910 Smallest Range II.py diff --git a/910 Smallest Range II.py b/910 Smallest Range II.py new file mode 100644 index 0000000..634993a --- /dev/null +++ b/910 Smallest Range II.py @@ -0,0 +1,73 @@ +#!/usr/bin/python3 +""" +Given an array A of integers, for each integer A[i] we need to choose either +x = -K or x = K, and add x to A[i] (only once). + +After this process, we have some array B. + +Return the smallest possible difference between the maximum value of B and the +minimum value of B. + +Example 1: + +Input: A = [1], K = 0 +Output: 0 +Explanation: B = [1] +Example 2: + +Input: A = [0,10], K = 2 +Output: 6 +Explanation: B = [2,8] +Example 3: + +Input: A = [1,3,6], K = 3 +Output: 3 +Explanation: B = [4,6,3] + +Note: +1 <= A.length <= 10000 +0 <= A[i] <= 10000 +0 <= K <= 10000 +""" +from typing import List + + +class Solution: + def smallestRangeII(self, A: List[int], K: int) -> int: + """ + Say A[i] is the largest i that goes up. A[i+1] would be the smallest + goes down + Then A[0] + K, A[i] + K, A[i+1] - K, A[A.length - 1] - K + """ + A.sort() + mn = min(A) + mx = max(A) + ret = mx - mn + for i in range(len(A) - 1): + cur_mx = max(mx - K, A[i] + K) + cur_mn = min(mn + K, A[i+1] - K) + ret = min(ret, cur_mx - cur_mn) + + return ret + + def smallestRangeII_error(self, A: List[int], K: int) -> int: + """ + find the min max is not enough, since the min max after +/- K may change + """ + mini = min(A) + maxa = max(A) + # mini + K, maxa - K + B = [] + max_upper_diff = 0 + max_lower_diff = 0 + upper = max(mini + K, maxa - K) # may cross + lower = min(mini + K, maxa - K) + for a in A: + diffs = [(a + K) - upper, lower - (a - K)] + cur_diff = min(diffs) + if cur_diff == diffs[0] and cur_diff >= max_upper_diff: + max_upper_diff = cur_diff + elif cur_diff == diffs[1] and cur_diff >= max_lower_diff: + max_lower_diff = cur_diff + + return upper + max_upper_diff - (lower + max_lower_diff) From cf8a3bc565840dbc444589c11857661b825dbdd2 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 12 Apr 2019 11:28:31 -0700 Subject: [PATCH 242/344] 901 --- 901 Online Stock Span.py | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 901 Online Stock Span.py diff --git a/901 Online Stock Span.py b/901 Online Stock Span.py new file mode 100644 index 0000000..6eae8d8 --- /dev/null +++ b/901 Online Stock Span.py @@ -0,0 +1,64 @@ +#!/usr/bin/python3 +""" +Write a class StockSpanner which collects daily price quotes for some stock, and +returns the span of that stock's price for the current day. + +The span of the stock's price today is defined as the maximum number of +consecutive days (starting from today and going backwards) for which the price +of the stock was less than or equal to today's price. + +For example, if the price of a stock over the next 7 days were [100, 80, 60, 70, +60, 75, 85], then the stock spans would be [1, 1, 1, 2, 1, 4, 6]. + +Example 1: + +Input: ["StockSpanner","next","next","next","next","next","next","next"], [[], +[100],[80],[60],[70],[60],[75],[85]] +Output: [null,1,1,1,2,1,4,6] +Explanation: +First, S = StockSpanner() is initialized. Then: +S.next(100) is called and returns 1, +S.next(80) is called and returns 1, +S.next(60) is called and returns 1, +S.next(70) is called and returns 2, +S.next(60) is called and returns 1, +S.next(75) is called and returns 4, +S.next(85) is called and returns 6. + +Note that (for example) S.next(75) returned 4, because the last 4 prices +(including today's price of 75) were less than or equal to today's price. + +Note: + +Calls to StockSpanner.next(int price) will have 1 <= price <= 10^5. +There will be at most 10000 calls to StockSpanner.next per test case. +There will be at most 150000 calls to StockSpanner.next across all test cases. +The total time limit for this problem has been reduced by 75% for C++, and 50% +for all other languages. +""" + + +class StockSpanner: + def __init__(self): + """ + Consecutive Backward <= + insort? O(n) or O(log n) using BST. Probably not for consecutive days + + Only interested in consecutive backwards <=, then only keep the + previously >. A stack to maintain a list ">" (decreasing) elements + """ + self.stk = [] # [(price, span)] + + def next(self, price: int) -> int: + cur_span = 1 + while self.stk and self.stk[-1][0] <= price: + _, span = self.stk.pop() + cur_span += span + self.stk.append((price, cur_span)) + return cur_span + + + +# Your StockSpanner object will be instantiated and called as such: +# obj = StockSpanner() +# param_1 = obj.next(price) From 55f3f93d5fa4d27d4f0e50a54c8c69d864345f3d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 12 Apr 2019 11:40:18 -0700 Subject: [PATCH 243/344] 925 --- 925 Long Pressed Name.py | 59 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 925 Long Pressed Name.py diff --git a/925 Long Pressed Name.py b/925 Long Pressed Name.py new file mode 100644 index 0000000..a857ab7 --- /dev/null +++ b/925 Long Pressed Name.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 +""" +Your friend is typing his name into a keyboard. Sometimes, when typing a +character c, the key might get long pressed, and the character will be typed 1 +or more times. + +You examine the typed characters of the keyboard. Return True if it is possible +that it was your friends name, with some characters (possibly none) being long +pressed. + +Example 1: + +Input: name = "alex", typed = "aaleex" +Output: true +Explanation: 'a' and 'e' in 'alex' were long pressed. +Example 2: + +Input: name = "saeed", typed = "ssaaedd" +Output: false +Explanation: 'e' must have been pressed twice, but it wasn't in the typed output. +Example 3: + +Input: name = "leelee", typed = "lleeelee" +Output: true +Example 4: + +Input: name = "laiden", typed = "laiden" +Output: true +Explanation: It's not necessary to long press any character. + +Note: + +name.length <= 1000 +typed.length <= 1000 +The characters of name and typed are lowercase letters. +""" + + +class Solution: + def isLongPressedName(self, name: str, typed: str) -> bool: + """ + two pointers + """ + m, n = len(name), len(typed) + i, j = 0, 0 + while i < m and j < n: + if name[i] == typed[j]: + i += 1 + j += 1 + elif j - 1 >= 0 and typed[j-1] == typed[j]: + j += 1 + else: + return False + + # tail + while j - 1 >= 0 and j < n and typed[j-1] == typed[j]: + j += 1 + + return i == m and j == n From 7eb419c925553ad3ba06262aef1e646bb1f7c2ff Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 12 Apr 2019 11:48:21 -0700 Subject: [PATCH 244/344] 929 --- 929 Unique Email Addresses.py | 59 +++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 929 Unique Email Addresses.py diff --git a/929 Unique Email Addresses.py b/929 Unique Email Addresses.py new file mode 100644 index 0000000..3353a11 --- /dev/null +++ b/929 Unique Email Addresses.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 +""" +Every email consists of a local name and a domain name, separated by the @ sign. + +For example, in alice@leetcode.com, alice is the local name, and leetcode.com is +the domain name. + +Besides lowercase letters, these emails may contain '.'s or '+'s. + +If you add periods ('.') between some characters in the local name part of an +email address, mail sent there will be forwarded to the same address without +dots in the local name. For example, "alice.z@leetcode.com" and +"alicez@leetcode.com" forward to the same email address. (Note that this rule +does not apply for domain names.) + +If you add a plus ('+') in the local name, everything after the first plus sign +will be ignored. This allows certain emails to be filtered, for example +m.y+name@email.com will be forwarded to my@email.com. (Again, this rule does +not apply for domain names.) + +It is possible to use both of these rules at the same time. + +Given a list of emails, we send one email to each address in the list. How many +different addresses actually receive mails? + +Example 1: + +Input: ["test.email+alex@leetcode.com","test.e.mail+bob.cathy@leetcode.com", +"testemail+david@lee.tcode.com"] +Output: 2 +Explanation: "testemail@leetcode.com" and "testemail@lee.tcode.com" actually +receive mails + +Note: + +1 <= emails[i].length <= 100 +1 <= emails.length <= 100 +Each emails[i] contains exactly one '@' character. +All local and domain names are non-empty. +Local names do not start with a '+' character. +""" +from typing import List + + +class Solution: + def numUniqueEmails(self, emails: List[str]) -> int: + """ + stemming + """ + s = set() + for e in emails: + local, domain = e.split("@") + local = self.stem(local) + s.add((local, domain)) + + return len(s) + + def stem(self, local): + return local.split("+")[0].replace(".", "") From c97076ee39f660acd6c79e886c7fd992a2c156db Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 12 Apr 2019 15:59:10 -0700 Subject: [PATCH 245/344] 908 --- 908 Smallest Range I.py | 41 ++++++++++++++++++++++ 958 Check Completeness of a Binary Tree.py | 9 ++--- 2 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 908 Smallest Range I.py diff --git a/908 Smallest Range I.py b/908 Smallest Range I.py new file mode 100644 index 0000000..b5446b4 --- /dev/null +++ b/908 Smallest Range I.py @@ -0,0 +1,41 @@ +#!/usr/bin/python3 +""" +Given an array A of integers, for each integer A[i] we may choose any x with +-K <= x <= K, and add x to A[i]. + +After this process, we have some array B. + +Return the smallest possible difference between the maximum value of B and the +minimum value of B. + +Example 1: + +Input: A = [1], K = 0 +Output: 0 +Explanation: B = [1] +Example 2: + +Input: A = [0,10], K = 2 +Output: 6 +Explanation: B = [2,8] +Example 3: + +Input: A = [1,3,6], K = 3 +Output: 0 +Explanation: B = [3,3,3] or B = [4,4,4] + + +Note: +1 <= A.length <= 10000 +0 <= A[i] <= 10000 +0 <= K <= 10000 +""" +from typing import List + + +class Solution: + def smallestRangeI(self, A: List[int], K: int) -> int: + """ + only need the max and min + """ + return max(0, max(A) - K - (min(A) + K)) diff --git a/958 Check Completeness of a Binary Tree.py b/958 Check Completeness of a Binary Tree.py index 4d6bff1..a739c75 100644 --- a/958 Check Completeness of a Binary Tree.py +++ b/958 Check Completeness of a Binary Tree.py @@ -36,15 +36,16 @@ def __init__(self): def isCompleteTree(self, root: TreeNode) -> bool: """ - Do it in one path - left first dfs - record the max depth and expecting partial fill in the last level + Do it in one pass + Left first dfs + Record the max depth and expecting partial fill in the last level + Need a special flag to tell whether expecting partial now """ return self.dfs(root, 0) def dfs(self, node, d): if not node: - # empty node is the key decision point + # empty node (below leaf) is the key decision point if self.max_depth == -float("inf"): # leftmost empty node self.max_depth = d - 1 return True From ff3f2a17ac11b3a0b5ad4baf7094060bcccad786 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 12 Apr 2019 16:32:39 -0700 Subject: [PATCH 246/344] 919 --- 919 Complete Binary Tree Inserter.py | 90 ++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 919 Complete Binary Tree Inserter.py diff --git a/919 Complete Binary Tree Inserter.py b/919 Complete Binary Tree Inserter.py new file mode 100644 index 0000000..2c4ec26 --- /dev/null +++ b/919 Complete Binary Tree Inserter.py @@ -0,0 +1,90 @@ +#!/usr/bin/python3 +""" +A complete binary tree is a binary tree in which every level, except possibly +the last, is completely filled, and all nodes are as far left as possible. + +Write a data structure CBTInserter that is initialized with a complete binary +tree and supports the following operations: + +CBTInserter(TreeNode root) initializes the data structure on a given tree with +head node root; +CBTInserter.insert(int v) will insert a TreeNode into the tree with value +node.val = v so that the tree remains complete, and returns the value of the +parent of the inserted TreeNode; +CBTInserter.get_root() will return the head node of the tree. + +Example 1: + +Input: inputs = ["CBTInserter","insert","get_root"], inputs = [[[1]],[2],[]] +Output: [null,1,[1,2]] +Example 2: + +Input: inputs = ["CBTInserter","insert","insert","get_root"], inputs = +[[[1,2,3,4,5,6]],[7],[8],[]] +Output: [null,3,4,[1,2,3,4,5,6,7,8]] + +Note: +The initial given tree is complete and contains between 1 and 1000 nodes. +CBTInserter.insert is called at most 10000 times per test case. +Every value of a given or inserted node is between 0 and 5000. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +from collections import deque + + +class CBTInserter: + def __init__(self, root: TreeNode): + """ + Maintain a dequeue of insertion candidates + Insertion candidates are non-full nodes (superset of leaf nodes) + BFS to get the insertion candidates + + During insertion, insert the node to the first insertion candidate's + child. Then, the inserting node is the last in the candidate queue + """ + self.candidates = deque() + self.root = root + q = [root] # can also use deque + while q: + cur_q = [] + for e in q: + if e.left: + cur_q.append(e.left) + if e.right: + cur_q.append(e.right) + if not e.left or not e.right: + # non-full node + self.candidates.append(e) + q = cur_q + + def insert(self, v: int) -> int: + pi = self.candidates[0] + node = TreeNode(v) + if not pi.left: + pi.left = node + else: + pi.right = node + + if pi.left and pi.right: + self.candidates.popleft() + + self.candidates.append(node) + return pi.val + + def get_root(self) -> TreeNode: + return self.root + + +# Your CBTInserter object will be instantiated and called as such: +# obj = CBTInserter(root) +# param_1 = obj.insert(v) +# param_2 = obj.get_root() From 57095994cec425f624b11f6932efe76874ee9671 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 12 Apr 2019 17:32:29 -0700 Subject: [PATCH 247/344] 934 --- 934 Shortest Bridge.py | 96 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 934 Shortest Bridge.py diff --git a/934 Shortest Bridge.py b/934 Shortest Bridge.py new file mode 100644 index 0000000..9a16268 --- /dev/null +++ b/934 Shortest Bridge.py @@ -0,0 +1,96 @@ +#!/usr/bin/python3 +""" +In a given 2D binary array A, there are two islands. (An island is a +4-directionally connected group of 1s not connected to any other 1s.) + +Now, we may change 0s to 1s so as to connect the two islands together to form 1 +island. + +Return the smallest number of 0s that must be flipped. (It is guaranteed that +the answer is at least 1.) + +Example 1: + +Input: [[0,1],[1,0]] +Output: 1 +Example 2: + +Input: [[0,1,0],[0,0,0],[0,0,1]] +Output: 2 +Example 3: + +Input: [[1,1,1,1,1],[1,0,0,0,1],[1,0,1,0,1],[1,0,0,0,1],[1,1,1,1,1]] +Output: 1 + +Note: +1 <= A.length = A[0].length <= 100 +A[i][j] == 0 or A[i][j] == 1 +""" +from typing import List + + +dirs = ((0, -1), (0, 1), (-1, 0), (1, 0)) + + +class Solution: + def shortestBridge(self, A: List[List[int]]) -> int: + """ + market component 1 and component 2 + iterate 0 and BFS, min(dist1 + dist2 - 1)? + O(N * N) high complexity + + BFS grow from 1 component + """ + m, n = len(A), len(A[0]) + # coloring + colors = [[None for _ in range(n)] for _ in range(m)] + color = 0 + for i in range(m): + for j in range(n): + if A[i][j] == 1 and colors[i][j] is None: + self.dfs(A, i, j, colors, color) + color += 1 + assert color == 2 + + # BFS + step = 0 + q = [] + visited = [[False for _ in range(n)] for _ in range(m)] + for i in range(m): + for j in range(n): + if colors[i][j] == 0: + visited[i][j] = True + q.append((i, j)) + + while q: + cur_q = [] + for i, j in q: + for I, J in self.nbr(A, i, j): + if not visited[I][J]: + if colors[I][J] == None: + visited[I][J] = True # pre-check, dedup + cur_q.append((I, J)) + elif colors[I][J] == 1: + return step + step += 1 + q = cur_q + + raise + + def nbr(self, A, i, j): + m, n = len(A), len(A[0]) + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < m and 0 <= J < n: + yield I, J + + def dfs(self, A, i, j, colors, color): + colors[i][j] = color + for I, J in self.nbr(A, i, j): + if colors[I][J] is None and A[I][J] == 1: + self.dfs(A, I, J, colors, color) + + +if __name__ == "__main__": + assert Solution().shortestBridge([[1,1,1,1,1],[1,0,0,0,1],[1,0,1,0,1],[1,0,0,0,1],[1,1,1,1,1]]) == 1 From 71bdb51a9c4f4debc5ec8c3b8392bf777a8b6802 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 12 Apr 2019 18:36:32 -0700 Subject: [PATCH 248/344] 961 --- 961 N-Repeated Element in Size 2N Array.py | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 961 N-Repeated Element in Size 2N Array.py diff --git a/961 N-Repeated Element in Size 2N Array.py b/961 N-Repeated Element in Size 2N Array.py new file mode 100644 index 0000000..3a8981b --- /dev/null +++ b/961 N-Repeated Element in Size 2N Array.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 +""" +In a array A of size 2N, there are N+1 unique elements, and exactly one of these +elements is repeated N times. + +Return the element repeated N times. + +Example 1: + +Input: [1,2,3,3] +Output: 3 +Example 2: + +Input: [2,1,2,5,3,2] +Output: 2 +Example 3: + +Input: [5,1,5,2,5,3,5,4] +Output: 5 + + +Note: + +4 <= A.length <= 10000 +0 <= A[i] < 10000 +A.length is even +""" +from typing import List + + +class Solution: + def repeatedNTimes(self, A: List[int]) -> int: + """ + Counter. Straightforward. O(N) space + + O(1) space + 2N items, N + 1 unique, 1 repeat N times + + N = 2 + a t b t + t a b t + N = 3 + a t b t c t + + window 2, cannot find the target + window 3, can find the target? no [9,5,6,9] + window 4, can find + + + * There is a major element in a length 2 subarray, or; + * Every length 2 subarray has exactly 1 major element, which means that + a length 4 subarray that begins at a major element will have 2 major + elements. + """ + n = len(A) + for i in range(n - 1): + for j in range(3): + if A[i] == A[min(n - 1, i + 1 + j)]: + return A[i] + + raise + + +if __name__ == "__main__": + assert Solution().repeatedNTimes([1,2,3,3]) == 3 From 7d935ebb264bdd2a8d4570e0a258afe6b74b23eb Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 12 Apr 2019 23:42:56 -0700 Subject: [PATCH 249/344] 1002 --- 1002 Find Common Characters.py | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 1002 Find Common Characters.py diff --git a/1002 Find Common Characters.py b/1002 Find Common Characters.py new file mode 100644 index 0000000..eeddfb4 --- /dev/null +++ b/1002 Find Common Characters.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +""" +Given an array A of strings made only from lowercase letters, return a list of +all characters that show up in all strings within the list (including +duplicates). For example, if a character occurs 3 times in all strings but not +4 times, you need to include that character three times in the final answer. + +You may return the answer in any order. + + + +Example 1: + +Input: ["bella","label","roller"] +Output: ["e","l","l"] +Example 2: + +Input: ["cool","lock","cook"] +Output: ["c","o"] + + +Note: + +1 <= A.length <= 100 +1 <= A[i].length <= 100 +A[i][j] is a lowercase letter +""" +import string + +from typing import List +from collections import Counter + + +class Solution: + def commonChars(self, A: List[str]) -> List[str]: + """ + running counter + """ + ret = [] + if not A: + return ret + + counter = Counter(A[0]) + for a in A[1:]: + cur = Counter(a) + for c in string.ascii_lowercase: + counter[c] = min(counter[c], cur[c]) + + for c in string.ascii_lowercase: + if counter[c] > 0: + ret.extend([c] * counter[c]) + + return ret From 800a1db79e96bb149b0009a5ce3f937bd25b5e87 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 12 Apr 2019 23:55:41 -0700 Subject: [PATCH 250/344] 1004 --- 1004 Max Consecutive Ones III.py | 65 ++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 1004 Max Consecutive Ones III.py diff --git a/1004 Max Consecutive Ones III.py b/1004 Max Consecutive Ones III.py new file mode 100644 index 0000000..8bc72d4 --- /dev/null +++ b/1004 Max Consecutive Ones III.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 +""" +Given an array A of 0s and 1s, we may change up to K values from 0 to 1. + +Return the length of the longest (contiguous) subarray that contains only 1s. + + + +Example 1: + +Input: A = [1,1,1,0,0,0,1,1,1,1,0], K = 2 +Output: 6 +Explanation: +[1,1,1,0,0,1,1,1,1,1,1] +Bolded numbers were flipped from 0 to 1. The longest subarray is underlined. +Example 2: + +Input: A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3 +Output: 10 +Explanation: +[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1] +Bolded numbers were flipped from 0 to 1. The longest subarray is underlined. + + +Note: +1 <= A.length <= 20000 +0 <= K <= A.length +A[i] is 0 or 1 +""" +from typing import List + + +class Solution: + def longestOnes(self, A: List[int], K: int) -> int: + """ + len(gap) + But there is multiple gap need to fill, and which gaps to fill is + undecided. Greedy? No. DP? + + Sliding Window: Find the longest subarray with at most K zeros. + """ + i, j = 0, 0 + cnt_0 = 0 + n = len(A) + ret = 0 + while i < n and j < n: + while j < n: + if A[j] == 0 and cnt_0 < K: + j += 1 + cnt_0 += 1 + elif A[j] == 1: + j += 1 + else: + break + + ret = max(ret, j - i) + if A[i] == 0: + cnt_0 -= 1 + i += 1 + + return ret + + +if __name__ == "__main__": + assert Solution().longestOnes([1,1,1,0,0,0,1,1,1,1,0], 2) == 6 From 523a3be522dafc30d442569c6b071347f3cd1ef3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 16 Apr 2019 21:24:43 -0700 Subject: [PATCH 251/344] 1005 --- 048 Pow(x, n).py | 60 +++++++++--------- ...Maximize Sum Of Array After K Negations.py | 61 +++++++++++++++++++ 146 LRU Cache py3.py | 2 + 289 Game of Life.py | 6 ++ 4 files changed, 100 insertions(+), 29 deletions(-) create mode 100644 1005 Maximize Sum Of Array After K Negations.py diff --git a/048 Pow(x, n).py b/048 Pow(x, n).py index 37f777b..2751c4c 100644 --- a/048 Pow(x, n).py +++ b/048 Pow(x, n).py @@ -2,7 +2,37 @@ Implement pow(x, n). """ __author__ = 'Danyang' + + class Solution: + def pow(self, x, n): + """ + O(log n) + Algorithm: math, Exponentiation by Squaring + + Basically: x^n = (x^2)^(n/2) + More formally: x^n = x^(n/2) * x^(n/2) * x^(n%2) + + :param x: float + :param n: integer + :return: float + """ + invert_flag = False if n > 0 else True + # O(log n) + n = abs(n) + product = 1.0 + while n > 0: + if n & 1 == 1: + product *= x + + n = n >> 1 + x *= x + + if invert_flag: + product = 1.0 / product + + return product + # @param x, a float # @param n, a integer # @return a float @@ -43,33 +73,5 @@ def pow_TLE(self, x, n): return product - - - - def pow(self, x, n): - """ - O(log n) - Algorithm: math, Exponentiation by Squaring - - Basically: x^n = (x^2)^(n/2) - More formally: x^n = x^(n/2) * x^(n/2) * x^(n%2) - - :param x: float - :param n: integer - :return: float - """ - invert_flag = False if n>0 else True - n = abs(n) - product = 1.0 - while n>0: - if n&1==1: - product *= x - n = n>>1 - x *=x - - if invert_flag: - product = 1.0/product - return product - if __name__=="__main__": - print Solution().pow(8.88023, 3) + print Solution().pow(8.88023, 3) diff --git a/1005 Maximize Sum Of Array After K Negations.py b/1005 Maximize Sum Of Array After K Negations.py new file mode 100644 index 0000000..a5950ae --- /dev/null +++ b/1005 Maximize Sum Of Array After K Negations.py @@ -0,0 +1,61 @@ +#!/usr/bin/python3 +""" +Given an array A of integers, we must modify the array in the following way: we +choose an i and replace A[i] with -A[i], and we repeat this process K times in +total. (We may choose the same index i multiple times.) + +Return the largest possible sum of the array after modifying it in this way. + + + +Example 1: + +Input: A = [4,2,3], K = 1 +Output: 5 +Explanation: Choose indices (1,) and A becomes [4,-2,3]. +Example 2: + +Input: A = [3,-1,0,2], K = 3 +Output: 6 +Explanation: Choose indices (1, 2, 2) and A becomes [3,1,0,2]. +Example 3: + +Input: A = [2,-3,-1,5,-4], K = 2 +Output: 13 +Explanation: Choose indices (1, 4) and A becomes [2,3,-1,5,4]. + + +Note: + +1 <= A.length <= 10000 +1 <= K <= 10000 +-100 <= A[i] <= 100 +""" +from typing import List + + +class Solution: + def largestSumAfterKNegations(self, A: List[int], K: int) -> int: + """ + revert all the negative, if positive, revert multiple times the smallest + + since -100 <= A[i] <= 100, then sort can be done in linear time + """ + A.sort() + for i in range(len(A)): + if K == 0: + break + + if A[i] < 0: + A[i] *= -1 + prev = A[i] + K -= 1 + else: + if K % 2 != 0: + if i - 1 >= 0 and A[i-1] < A[i]: + A[i-1] *= -1 + else: + A[i] *= -1 + break + + return sum(A) diff --git a/146 LRU Cache py3.py b/146 LRU Cache py3.py index 99bc489..3ef268e 100644 --- a/146 LRU Cache py3.py +++ b/146 LRU Cache py3.py @@ -44,6 +44,8 @@ def __init__(self, capacity: int): But Single linked list is not enough then Double Linked List Need dummy head and tail to avoid over complication of null checking + + Essentially it is the OrderedDict """ self.head = Node(None, None) self.tail = Node(None, None) diff --git a/289 Game of Life.py b/289 Game of Life.py index 7304e37..d1df353 100644 --- a/289 Game of Life.py +++ b/289 Game of Life.py @@ -34,6 +34,12 @@ def gameOfLife(self, board): Similar to dp space optimization. 1. Line buffer, directional, main the entires for previous state. 2. higher bit, since you got 32-bit int + + + new new new + new cur pre + pre pre pre + :type board: List[List[int]] :rtype: void Do not return anything, modify board in-place instead. """ From 9eddbd092fc99c816065229499d8da3d3ea33d43 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 16 Apr 2019 21:59:26 -0700 Subject: [PATCH 252/344] 1008 --- ...ary Search Tree from Preorder Traversal.py | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 1008 Construct Binary Search Tree from Preorder Traversal.py diff --git a/1008 Construct Binary Search Tree from Preorder Traversal.py b/1008 Construct Binary Search Tree from Preorder Traversal.py new file mode 100644 index 0000000..805456f --- /dev/null +++ b/1008 Construct Binary Search Tree from Preorder Traversal.py @@ -0,0 +1,81 @@ +#!/usr/bin/python3 +""" +Return the root node of a binary search tree that matches the given preorder +traversal. + +(Recall that a binary search tree is a binary tree where for every node, any +descendant of node.left has a value < node.val, and any descendant of node.right +has a value > node.val. Also recall that a preorder traversal displays the +value of the node first, then traverses node.left, then traverses node.right.) + +Example 1: + +Input: [8,5,1,7,10,12] +Output: [8,5,10,1,7,null,12] + +Note: +1 <= preorder.length <= 100 +The values of preorder are distinct. +""" +from typing import List + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def bstFromPreorder2(self, preorder: List[int]) -> TreeNode: + """ + need to be BST + + scan the list to break left and right part + F(n) = 2 F(n/2) + O(n), then it is O(n log n) + + Make it O(n) + maintain a stack + After walking through example, left child can be determined quickly + since it is pre-order. Left comes first. + + Stack maintain a node that is missing right child + decreasing stack + """ + root = TreeNode(preorder[0]) + stk = [root] + for a in preorder[1:]: + node = TreeNode(a) + if a < stk[-1].val: # len(stk) always >= 1 + stk[-1].left = node + else: + while len(stk) >= 2 and stk[-2].val < a: + stk.pop() + + stk[-1].right = node + stk.pop() + + stk.append(node) + + return root + + def bstFromPreorder(self, preorder: List[int]) -> TreeNode: + """ + If a node is a right child (larger), find the proper parent + The proper parent should the deepest in the stack that its val < current val + """ + root = TreeNode(preorder[0]) + stk = [root] + for a in preorder[1:]: + node = TreeNode(a) + if a < stk[-1].val: + stk[-1].left = node + else: + while stk and stk[-1].val < a: + pi = stk.pop() + pi.right = node + stk.append(node) + + return root From 085efa6940a2cbfe39fc320cfc1a7595953a30d8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 16 Apr 2019 22:08:19 -0700 Subject: [PATCH 253/344] 1009 --- 1009 Complement of Base 10 Integer.py | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 1009 Complement of Base 10 Integer.py diff --git a/1009 Complement of Base 10 Integer.py b/1009 Complement of Base 10 Integer.py new file mode 100644 index 0000000..d04bd87 --- /dev/null +++ b/1009 Complement of Base 10 Integer.py @@ -0,0 +1,49 @@ +#!/usr/bin/python3 +""" +Every non-negative integer N has a binary representation. For example, 5 can be +represented as "101" in binary, 11 as "1011" in binary, and so on. Note that +except for N = 0, there are no leading zeroes in any binary representation. + +The complement of a binary representation is the number in binary you get when +changing every 1 to a 0 and 0 to a 1. For example, the complement of "101" in +binary is "010" in binary. + +For a given number N in base-10, return the complement of it's binary +representation as a base-10 integer. + +Example 1: +Input: 5 +Output: 2 +Explanation: 5 is "101" in binary, with complement "010" in binary, which is 2 +in base-10. + +Example 2: +Input: 7 +Output: 0 +Explanation: 7 is "111" in binary, with complement "000" in binary, which is 0 +in base-10. + +Example 3: +Input: 10 +Output: 5 +Explanation: 10 is "1010" in binary, with complement "0101" in binary, which is +5 in base-10. + +Note: +0 <= N < 10^9 +""" + + +class Solution: + def bitwiseComplement(self, N: int) -> int: + """ + invert the bit, and the mask it + """ + mask = 1 + cur = N + while cur >> 1: + cur >>= 1 + mask <<= 1 + mask += 1 + + return ~N & mask From 3863ef386e07b945b718a0ad44b026b8c1b611fd Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 16 Apr 2019 22:35:15 -0700 Subject: [PATCH 254/344] 1010 --- ...gs With Total Durations Divisible by 60.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 1010 Pairs of Songs With Total Durations Divisible by 60.py diff --git a/1010 Pairs of Songs With Total Durations Divisible by 60.py b/1010 Pairs of Songs With Total Durations Divisible by 60.py new file mode 100644 index 0000000..f0c7985 --- /dev/null +++ b/1010 Pairs of Songs With Total Durations Divisible by 60.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 +""" +In a list of songs, the i-th song has a duration of time[i] seconds. + +Return the number of pairs of songs for which their total duration in seconds is +divisible by 60. Formally, we want the number of indices i < j with (time[i] + +time[j]) % 60 == 0. + +Example 1: + +Input: [30,20,150,100,40] +Output: 3 +Explanation: Three pairs have a total duration divisible by 60: +(time[0] = 30, time[2] = 150): total duration 180 +(time[1] = 20, time[3] = 100): total duration 120 +(time[1] = 20, time[4] = 40): total duration 60 +Example 2: + +Input: [60,60,60] +Output: 3 +Explanation: All three pairs have a total duration of 120, which is divisible by 60. + +Note: +1 <= time.length <= 60000 +1 <= time[i] <= 500 +""" +from typing import List +from collections import defaultdict + + +class Solution: + def numPairsDivisibleBy60(self, time: List[int]) -> int: + """ + Running attribution + """ + counter = defaultdict(int) + ret = 0 + for t in time: + ret += counter[(60 - t) % 60] # handle 0 + counter[t % 60] += 1 + + return ret + + + def numPairsDivisibleBy60_error(self, time: List[int]) -> int: + """ + O(N^2) check i, j + Reduce it + O(N) by using hashmap, the key should mod 60 + + attribution error + """ + hm = defaultdict(int) + for t in time: + hm[t % 60] += 1 + + ret = 0 + for k, v in hm.items(): + if k == 0: + ret += (v * (v - 1)) // 2 + elif k <= 60 - k: # attribution + v2 = hm[60 - k] + ret += v2 * v + + return ret From f4fca1c9fc10e2b1d466d805750f574cd5ecca50 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 16 Apr 2019 23:10:02 -0700 Subject: [PATCH 255/344] 1011 --- ...Capacity To Ship Packages Within D Days.py | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 1011 Capacity To Ship Packages Within D Days.py diff --git a/1011 Capacity To Ship Packages Within D Days.py b/1011 Capacity To Ship Packages Within D Days.py new file mode 100644 index 0000000..07a32a7 --- /dev/null +++ b/1011 Capacity To Ship Packages Within D Days.py @@ -0,0 +1,82 @@ +#!/usr/bin/python3 +""" +A conveyor belt has packages that must be shipped from one port to another +within D days. + +The i-th package on the conveyor belt has a weight of weights[i]. Each day, we +load the ship with packages on the conveyor belt (in the order given by +weights). We may not load more weight than the maximum weight capacity of the +ship. + +Return the least weight capacity of the ship that will result in all the +packages on the conveyor belt being shipped within D days. + +Example 1: + +Input: weights = [1,2,3,4,5,6,7,8,9,10], D = 5 +Output: 15 +Explanation: +A ship capacity of 15 is the minimum to ship all the packages in 5 days like +this: +1st day: 1, 2, 3, 4, 5 +2nd day: 6, 7 +3rd day: 8 +4th day: 9 +5th day: 10 + +Note that the cargo must be shipped in the order given, so using a ship of +capacity 14 and splitting the packages into parts like (2, 3, 4, 5), (1, 6, 7), +(8), (9), (10) is not allowed. +Example 2: + +Input: weights = [3,2,2,4,1,4], D = 3 +Output: 6 +Explanation: +A ship capacity of 6 is the minimum to ship all the packages in 3 days like +this: +1st day: 3, 2 +2nd day: 2, 4 +3rd day: 1, 4 +Example 3: + +Input: weights = [1,2,3,1,1], D = 4 +Output: 3 +Explanation: +1st day: 1 +2nd day: 2 +3rd day: 3 +4th day: 1, 1 + +Note: + +1 <= D <= weights.length <= 50000 +1 <= weights[i] <= 500 +""" +from tying import List + + +class Solution: + def shipWithinDays(self, weights: List[int], D: int) -> int: + """ + Must respect conveyor's order + + Binary search on value range (max, sum) + """ + lo = max(weights) + hi = sum(weights) + while lo < hi: + mid = (lo + hi) // 2 + cnt = 1 + cur = 0 + for w in weights: + cur += w + if cur > mid: + cnt += 1 + cur = w + + if cnt > D: + lo = mid + 1 + else: + hi = mid + + return lo From b35a241daff95dcd3454fd1e5a1f279b44c2eb5d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 17 Apr 2019 22:16:51 -0700 Subject: [PATCH 256/344] 1013 --- ...n Array Into Three Parts With Equal Sum.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 1013 Partition Array Into Three Parts With Equal Sum.py diff --git a/1013 Partition Array Into Three Parts With Equal Sum.py b/1013 Partition Array Into Three Parts With Equal Sum.py new file mode 100644 index 0000000..b09b16a --- /dev/null +++ b/1013 Partition Array Into Three Parts With Equal Sum.py @@ -0,0 +1,55 @@ +#!/usr/bin/python3 +""" +Given an array A of integers, return true if and only if we can partition the +array into three non-empty parts with equal sums. + +Formally, we can partition the array if we can find indexes i+1 < j with (A[0] ++ A[1] + ... + A[i] == A[i+1] + A[i+2] + ... + A[j-1] == A[j] + A[j-1] + ... + +A[A.length - 1]) + +Example 1: + +Input: [0,2,1,-6,6,-7,9,1,2,0,1] +Output: true +Explanation: 0 + 2 + 1 = -6 + 6 - 7 + 9 + 1 = 2 + 0 + 1 +Example 2: + +Input: [0,2,1,-6,6,7,9,-1,2,0,1] +Output: false +Example 3: + +Input: [3,3,6,5,-2,2,5,1,-9,4] +Output: true +Explanation: 3 + 3 = 6 = 5 - 2 + 2 + 5 + 1 - 9 + 4 + +Note: + +3 <= A.length <= 50000 +-10000 <= A[i] <= 10000 +""" +from typing import List + + +class Solution: + def canThreePartsEqualSum(self, A: List[int]) -> bool: + s = sum(A) + if s % 3 != 0: + return False + + target = s // 3 + count = 0 + cur_sum = 0 + for a in A: + cur_sum += a + if cur_sum == target: + count += 1 + cur_sum = 0 + # elif cur_sum > target: + # return False + # can have negative number + + return count == 3 and cur_sum == 0 + + +if __name__ == "__main__": + assert Solution().canThreePartsEqualSum([3,3,6,5,-2,2,5,1,-9,4]) == True From 759d865b40131c3625cf9bf358353796908d124d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 17 Apr 2019 22:45:15 -0700 Subject: [PATCH 257/344] 1014 --- 1014 Best Sightseeing Pair.py | 69 +++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 1014 Best Sightseeing Pair.py diff --git a/1014 Best Sightseeing Pair.py b/1014 Best Sightseeing Pair.py new file mode 100644 index 0000000..480bda2 --- /dev/null +++ b/1014 Best Sightseeing Pair.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 +""" +Given an array A of positive integers, A[i] represents the value of the i-th +sightseeing spot, and two sightseeing spots i and j have distance j - i between +them. + +The score of a pair (i < j) of sightseeing spots is (A[i] + A[j] + i - j) : the +sum of the values of the sightseeing spots, minus the distance between them. + +Return the maximum score of a pair of sightseeing spots. + +Example 1: + +Input: [8,1,5,2,6] +Output: 11 +Explanation: i = 0, j = 2, A[i] + A[j] + i - j = 8 + 5 + 0 - 2 = 11 + + +Note: +2 <= A.length <= 50000 +1 <= A[i] <= 1000 +""" +from typing import List + + +class Solution: + def maxScoreSightseeingPair(self, A: List[int]) -> int: + """ + Attribute the result to the ending element + + Count the current best score in all previous. + Distance will increase, then the score will decay + """ + ret = -float("inf") + prev_max = A[0] + for a in A[1:]: + ret = max(ret, prev_max - 1 + a) + prev_max = max(prev_max - 1, a) + + return ret + + def maxScoreSightseeingPair_errpr(self, A: List[int]) -> int: + """ + brute force O(N^2) + + pre-processing, adjust A[i] as A[i] - i + error, no direction + """ + n = len(A) + B = [] + for i in range(n): + B.append(A[i] - i) + + # find top 2 + m1, m2 = None, None + for i in range(n): + if m1 is None: + m1 = i + elif m2 is None: + m2 = i + elif B[i] + (i - m1) > B[m1]: + m1 = i + elif B[i] + (i - m2) > B[m2]: + m2 = i + return A[m2] + A[m1] - abs(m2 - m1) + + +if __name__ == "__main__": + assert Solution().maxScoreSightseeingPair([8,1,5,2,6]) == 11 From 995a03c5165ad44001745df24bf723758adad02d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 18 Apr 2019 22:55:48 -0700 Subject: [PATCH 258/344] 1018 --- 1018 Binary Prefix Divisible By 5.py | 52 ++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 1018 Binary Prefix Divisible By 5.py diff --git a/1018 Binary Prefix Divisible By 5.py b/1018 Binary Prefix Divisible By 5.py new file mode 100644 index 0000000..c8acb7a --- /dev/null +++ b/1018 Binary Prefix Divisible By 5.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 +""" +Given an array A of 0s and 1s, consider N_i: the i-th subarray from A[0] to A[i] +interpreted as a binary number (from most-significant-bit to +least-significant-bit.) + +Return a list of booleans answer, where answer[i] is true if and only if N_i is +divisible by 5. + +Example 1: + +Input: [0,1,1] +Output: [true,false,false] +Explanation: +The input numbers in binary are 0, 01, 011; which are 0, 1, and 3 in base-10. +Only the first number is divisible by 5, so answer[0] is true. +Example 2: + +Input: [1,1,1] +Output: [false,false,false] +Example 3: + +Input: [0,1,1,1,1,1] +Output: [true,false,false,false,true,false] +Example 4: + +Input: [1,1,1,0,1] +Output: [false,false,false,false,false] + +Note: +1 <= A.length <= 30000 +A[i] is 0 or 1 +""" +from typing import List + + +class Solution: + def prefixesDivBy5(self, A: List[int]) -> List[bool]: + """ + brute force + """ + cur = 0 + ret = [] + for a in A: + cur = cur << 1 + a + cur %= 5 + if cur == 0: + ret.append(True) + else: + ret.append(False) + + return ret From 25a6941cc6caecfb91413d6449d769138f6587af Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 18 Apr 2019 22:58:02 -0700 Subject: [PATCH 259/344] update --- 1014 Best Sightseeing Pair.py | 2 +- 1018 Binary Prefix Divisible By 5.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/1014 Best Sightseeing Pair.py b/1014 Best Sightseeing Pair.py index 480bda2..8673420 100644 --- a/1014 Best Sightseeing Pair.py +++ b/1014 Best Sightseeing Pair.py @@ -39,7 +39,7 @@ def maxScoreSightseeingPair(self, A: List[int]) -> int: return ret - def maxScoreSightseeingPair_errpr(self, A: List[int]) -> int: + def maxScoreSightseeingPair_error(self, A: List[int]) -> int: """ brute force O(N^2) diff --git a/1018 Binary Prefix Divisible By 5.py b/1018 Binary Prefix Divisible By 5.py index c8acb7a..5c7d2bd 100644 --- a/1018 Binary Prefix Divisible By 5.py +++ b/1018 Binary Prefix Divisible By 5.py @@ -42,7 +42,7 @@ def prefixesDivBy5(self, A: List[int]) -> List[bool]: cur = 0 ret = [] for a in A: - cur = cur << 1 + a + cur = (cur << 1) + a cur %= 5 if cur == 0: ret.append(True) From 32d240630fb00cf67f5504e780445a8bfafa568c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 18 Apr 2019 23:27:11 -0700 Subject: [PATCH 260/344] 1019 --- 1019 Next Greater Node In Linked List.py | 65 ++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 1019 Next Greater Node In Linked List.py diff --git a/1019 Next Greater Node In Linked List.py b/1019 Next Greater Node In Linked List.py new file mode 100644 index 0000000..2ffaccf --- /dev/null +++ b/1019 Next Greater Node In Linked List.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 +""" +We are given a linked list with head as the first node. Let's number the nodes +in the list: node_1, node_2, node_3, ... etc. + +Each node may have a next larger value: for node_i, next_larger(node_i) is the +node_j.val such that j > i, node_j.val > node_i.val, and j is the smallest +possible choice. If such a j does not exist, the next larger value is 0. + +Return an array of integers answer, where answer[i] = next_larger(node_{i+1}). + +Note that in the example inputs (not outputs) below, arrays such as [2,1,5] +represent the serialization of a linked list with a head node value of 2, second +node value of 1, and third node value of 5. + +Example 1: +Input: [2,1,5] +Output: [5,5,0] + +Example 2: +Input: [2,7,4,3,5] +Output: [7,0,5,5,0] + +Example 3: +Input: [1,7,5,1,9,2,5,1] +Output: [7,9,9,9,0,5,0,0] + +Note: +1 <= node.val <= 10^9 for each node in the linked list. +The given list has length in the range [0, 10000]. +""" + +# Definition for singly-linked list. +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + +from typing import List + + +class Solution: + def nextLargerNodes(self, head: ListNode) -> List[int]: + """ + If input is an array, use stack from right to left. Maintain a decreasing stack + + How to make it one-pass? Maintain a stack from left to right for the element + waiting for the next larger node + """ + ret = [] + stk = [] # [[index, value]] + i = 0 + cur = head + while cur: + while stk and stk[-1][1] < cur.val: + idx, _ = stk.pop() + ret[idx] = cur.val + + stk.append([i, cur.val]) + ret.append(0) + cur = cur.next + i += 1 + + return ret From 6d5957118c30a1c81192eeb9169ea67a0aed3a3c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 19 Apr 2019 22:32:00 -0700 Subject: [PATCH 261/344] 1020 --- 1020 Number of Enclaves.py | 98 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 1020 Number of Enclaves.py diff --git a/1020 Number of Enclaves.py b/1020 Number of Enclaves.py new file mode 100644 index 0000000..2e43d84 --- /dev/null +++ b/1020 Number of Enclaves.py @@ -0,0 +1,98 @@ +#!/usr/bin/python3 +""" +Given a 2D array A, each cell is 0 (representing sea) or 1 (representing land) + +A move consists of walking from one land square 4-directionally to another land +square, or off the boundary of the grid. + +Return the number of land squares in the grid for which we cannot walk off the +boundary of the grid in any number of moves. + +Example 1: +Input: [[0,0,0,0],[1,0,1,0],[0,1,1,0],[0,0,0,0]] +Output: 3 +Explanation: +There are three 1s that are enclosed by 0s, and one 1 that isn't enclosed +because its on the boundary. + +Example 2: +Input: [[0,1,1,0],[0,0,1,0],[0,0,1,0],[0,0,0,0]] +Output: 0 +Explanation: +All 1s are either on the boundary or can reach the boundary. + +Note: +1 <= A.length <= 500 +1 <= A[i].length <= 500 +0 <= A[i][j] <= 1 +All rows have the same size. +""" +from typing import List + + +dirs = ((0, -1), (0, 1), (1, 0), (-1, 0)) + + +class Solution: + def numEnclaves(self, A: List[List[int]]) -> int: + """ + not dfs from any cell, but dfs from the edges + """ + m, n = len(A), len(A[0]) + visited = [[False for _ in range(n)] for _ in range(m)] + for i in range(m): + for j in range(n): + if not visited[i][j] and A[i][j] == 1 and (i == 0 or j == 0 or i == m - 1 or j == n - 1): + self.dfs(A, i, j, visited) + + ret = 0 + for i in range(m): + for j in range(n): + if A[i][j] == 1 and not visited[i][j]: + ret += 1 + return ret + + def dfs(self, A, i, j, visited): + m, n = len(A), len(A[0]) + visited[i][j] = True + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < m and 0 <= J < n and not visited[I][J] and A[I][J] == 1: + self.dfs(A, I, J, visited) + + +class SolutionError: + def __init__(self): + self.ret = 0 + + def numEnclaves(self, A: List[List[int]]) -> int: + """ + dfs coloring + """ + m, n = len(A), len(A[0]) + visited = [[None for _ in range(n)] for _ in range(m)] # 0 not off, 1 off + for i in range(m): + for j in range(n): + if not visited[i][j] and A[i][j] == 1: + self.dfs(A, i, j, visited) + return self.ret + + def dfs(self, A, i, j, visited): + m, n = len(A), len(A[0]) + visited[i][j] = 0 + for di, dj in dirs: + I = i + di + J = j + dj + if not (0 <= I < m and 0 <= J < n): + visited[i][j] = 1 + return True + if visited[I][J] == 1: + visited[i][j] = 1 + return True + if visited[I][J] is None and A[I][J] == 1 and self.dfs(A, I, J, visited): + visited[i][j] = 1 + return True + + self.ret += 1 + return False From d5129b3621cf99b7114ec1a6e154e4122ca168ac Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 19 Apr 2019 22:32:40 -0700 Subject: [PATCH 262/344] 1022 --- 1022 Sum of Root To Leaf Binary Numbers.py | 61 ++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 1022 Sum of Root To Leaf Binary Numbers.py diff --git a/1022 Sum of Root To Leaf Binary Numbers.py b/1022 Sum of Root To Leaf Binary Numbers.py new file mode 100644 index 0000000..f5de32e --- /dev/null +++ b/1022 Sum of Root To Leaf Binary Numbers.py @@ -0,0 +1,61 @@ +#!/usr/bin/python3 +""" +Given a binary tree, each node has value 0 or 1. Each root-to-leaf path +represents a binary number starting with the most significant bit. For example, +if the path is 0 -> 1 -> 1 -> 0 -> 1, then this could represent 01101 in binary, +which is 13. + +For all leaves in the tree, consider the numbers represented by the path from +the root to that leaf. + +Return the sum of these numbers. + +Example 1: +Input: [1,0,1,0,1,0,1] +Output: 22 +Explanation: (100) + (101) + (110) + (111) = 4 + 5 + 6 + 7 = 22 + +Note: +The number of nodes in the tree is between 1 and 1000. +node.val is 0 or 1. +The answer will not exceed 2^31 - 1. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.ret = 0 + self.lst = [] + + def sumRootToLeaf(self, root: TreeNode) -> int: + """ + Brute force, keep a lst, space O(log n) + Error-prone + """ + self.dfs(root) + return self.ret + + def dfs(self, node): + if not node: + return + + self.lst.append(node.val) # error prone + if not node.left and not node.right: + # leaf + cur = 0 + for a in self.lst: + cur <<= 1 + cur += a + self.ret += cur + else: + self.dfs(node.left) + self.dfs(node.right) + self.lst.pop() From 33a5345c41fa3fe48447965aa239e4c75552a273 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 19 Apr 2019 23:08:09 -0700 Subject: [PATCH 263/344] 1023 --- 1023 Camelcase Matching.py | 71 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 1023 Camelcase Matching.py diff --git a/1023 Camelcase Matching.py b/1023 Camelcase Matching.py new file mode 100644 index 0000000..b3becd7 --- /dev/null +++ b/1023 Camelcase Matching.py @@ -0,0 +1,71 @@ +#!/usr/bin/python3 +""" +A query word matches a given pattern if we can insert lowercase letters to the +pattern word so that it equals the query. (We may insert each character at any +position, and may insert 0 characters.) + +Given a list of queries, and a pattern, return an answer list of booleans, where +answer[i] is true if and only if queries[i] matches the pattern. + +Example 1: + +Input: queries = ["FooBar","FooBarTest","FootBall","FrameBuffer", +"ForceFeedBack"], pattern = "FB" +Output: [true,false,true,true,false] +Explanation: +"FooBar" can be generated like this "F" + "oo" + "B" + "ar". +"FootBall" can be generated like this "F" + "oot" + "B" + "all". +"FrameBuffer" can be generated like this "F" + "rame" + "B" + "uffer". +Example 2: + +Input: queries = ["FooBar","FooBarTest","FootBall","FrameBuffer", +"ForceFeedBack"], pattern = "FoBa" +Output: [true,false,true,false,false] +Explanation: +"FooBar" can be generated like this "Fo" + "o" + "Ba" + "r". +"FootBall" can be generated like this "Fo" + "ot" + "Ba" + "ll". +Example 3: + +Input: queries = ["FooBar","FooBarTest","FootBall","FrameBuffer", +"ForceFeedBack"], pattern = "FoBaT" +Output: [false,true,false,false,false] +Explanation: +"FooBarTest" can be generated like this "Fo" + "o" + "Ba" + "r" + "T" + "est". + +Note: +1 <= queries.length <= 100 +1 <= queries[i].length <= 100 +1 <= pattern.length <= 100 +All strings consists only of lower and upper case English letters. +""" +from typing import List + + +class Solution: + def camelMatch(self, queries: List[str], pattern: str) -> List[bool]: + ret = [] + for q in queries: + ret.append(self.match(q, pattern)) + + return ret + + def match(self, q, p): + i = 0 + j = 0 + while i < len(q) and j < len(p): + if q[i] == p[j]: + i += 1 + j += 1 + elif q[i].islower(): + i += 1 + else: + break + + while i < len(q) and q[i].islower(): + i += 1 + + return i == len(q) and j == len(p) + + +if __name__ == "__main__": + assert Solution().camelMatch(["FooBar","FooBarTest","FootBall","FrameBuffer","ForceFeedBack"], "FoBa") == [True, False, True, False, False] From 6b593dc9dc36ee0227210c315e0b4fef9356db9c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 19 Apr 2019 23:16:31 -0700 Subject: [PATCH 264/344] 1026 --- ...um Difference Between Node and Ancestor.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 1026 Maximum Difference Between Node and Ancestor.py diff --git a/1026 Maximum Difference Between Node and Ancestor.py b/1026 Maximum Difference Between Node and Ancestor.py new file mode 100644 index 0000000..cee7616 --- /dev/null +++ b/1026 Maximum Difference Between Node and Ancestor.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 +""" +Given the root of a binary tree, find the maximum value V for which there exists +different nodes A and B where V = |A.val - B.val| and A is an ancestor of B. + +(A node A is an ancestor of B if either: any child of A is equal to B, or any +child of A is an ancestor of B.) + +Example 1: +Input: [8,3,10,1,6,null,14,null,null,4,7,13] +Output: 7 +Explanation: +We have various ancestor-node differences, some of which are given below : +|8 - 3| = 5 +|3 - 7| = 4 +|8 - 1| = 7 +|10 - 13| = 3 +Among all possible differences, the maximum value of 7 is obtained by |8 - 1| = +7. + +Note: +The number of nodes in the tree is between 2 and 5000. +Each node will have value between 0 and 100000. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.ret = 0 + + def maxAncestorDiff(self, root: TreeNode) -> int: + """ + dfs return min and max + """ + self.dfs(root) + return self.ret + + def dfs(self, node): + if not node: + return float("inf"), -float("inf") + + lmin, lmax = self.dfs(node.left) + rmin, rmax = self.dfs(node.right) + mini = min(lmin, rmin) + maxa = max(lmax, rmax) + if mini != float("inf"): + self.ret = max(self.ret, abs(mini - node.val)) + if maxa != -float("inf"): + self.ret = max(self.ret, abs(maxa - node.val)) + + return min(mini, node.val), max(maxa, node.val) From 7e9e40c6f8bb35f00aa7cf4b360239586d37b11f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 19 Apr 2019 23:36:31 -0700 Subject: [PATCH 265/344] 1027 --- 1027 Longest Arithmetic Sequence.py | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 1027 Longest Arithmetic Sequence.py diff --git a/1027 Longest Arithmetic Sequence.py b/1027 Longest Arithmetic Sequence.py new file mode 100644 index 0000000..7e784a5 --- /dev/null +++ b/1027 Longest Arithmetic Sequence.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 +""" +Given an array A of integers, return the length of the longest arithmetic +subsequence in A. + +Recall that a subsequence of A is a list A[i_1], A[i_2], ..., A[i_k] with +0 <= i_1 < i_2 < ... < i_k <= A.length - 1, and that a sequence B is arithmetic +if B[i+1] - B[i] are all the same value (for 0 <= i < B.length - 1). + +Example 1: +Input: [3,6,9,12] +Output: 4 +Explanation: +The whole array is an arithmetic sequence with steps of length = 3. + +Example 2: +Input: [9,4,7,2,10] +Output: 3 +Explanation: +The longest arithmetic subsequence is [4,7,10]. + +Example 3: +Input: [20,1,15,3,10,5,8] +Output: 4 +Explanation: +The longest arithmetic subsequence is [20,15,10,5]. + +Note: +2 <= A.length <= 2000 +0 <= A[i] <= 10000 +""" +from typing import List +from collections import defaultdict + + +class Solution: + def longestArithSeqLength(self, A: List[int]) -> int: + """ + Brute force O(n^2) + + Let F[i][j] be the longest arith subseq ending at A[i] with step j + """ + F = defaultdict(lambda: defaultdict(lambda: 1)) + for i in range(len(A)): + for j in range(i): + delta = A[i] - A[j] + F[i][delta] = F[j][delta] + 1 + + ret = 0 + for d in F.values(): + for v in d.values(): + ret = max(ret, v) + + return ret + + +if __name__ == "__main__": + assert Solution().longestArithSeqLength([20,1,15,3,10,5,8]) == 4 From e942a4941c14fc405c6fba8173b52188c7da1dcf Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 20 Apr 2019 00:30:47 -0700 Subject: [PATCH 266/344] 1024 --- 1024 Video Stitching.py | 94 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 1024 Video Stitching.py diff --git a/1024 Video Stitching.py b/1024 Video Stitching.py new file mode 100644 index 0000000..2b04a22 --- /dev/null +++ b/1024 Video Stitching.py @@ -0,0 +1,94 @@ +#!/usr/bin/python3 +""" +You are given a series of video clips from a sporting event that lasted T +seconds. These video clips can be overlapping with each other and have varied +lengths. + +Each video clip clips[i] is an interval: it starts at time clips[i][0] and ends +at time clips[i][1]. We can cut these clips into segments freely: for example, +a clip [0, 7] can be cut into segments [0, 1] + [1, 3] + [3, 7]. + +Return the minimum number of clips needed so that we can cut the clips into +segments that cover the entire sporting event ([0, T]). If the task is +impossible, return -1. + +Example 1: +Input: clips = [[0,2],[4,6],[8,10],[1,9],[1,5],[5,9]], T = 10 +Output: 3 +Explanation: +We take the clips [0,2], [8,10], [1,9]; a total of 3 clips. +Then, we can reconstruct the sporting event as follows: +We cut [1,9] into segments [1,2] + [2,8] + [8,9]. +Now we have segments [0,2] + [2,8] + [8,10] which cover the sporting event [0, 10]. +Example 2: + +Input: clips = [[0,1],[1,2]], T = 5 +Output: -1 +Explanation: +We can't cover [0,5] with only [0,1] and [0,2]. +Example 3: + +Input: clips = [[0,1],[6,8],[0,2],[5,6],[0,4],[0,3],[6,7],[1,3],[4,7],[1,4],[2,5],[2,6],[3,4],[4,5],[5,7],[6,9]], T = 9 +Output: 3 +Explanation: +We can take clips [0,4], [4,7], and [6,9]. +Example 4: + +Input: clips = [[0,4],[2,8]], T = 5 +Output: 2 +Explanation: +Notice you can have extra video after the event ends. + +Note: +1 <= clips.length <= 100 +0 <= clips[i][0], clips[i][1] <= 100 +0 <= T <= 100 +""" +from typing import List + + +class Solution: + def videoStitching(self, clips: List[List[int]], T: int) -> int: + """ + Greedy is correct. The larger the coverage, the better + """ + clips.sort() + prev_e = 0 + ret = 0 + + i = 0 + while i < len(clips): + if clips[i][0] > prev_e: # gap + break + + max_e = -float("inf") + while i < len(clips) and clips[i][0] <= prev_e: + max_e = max(max_e, clips[i][1]) + i += 1 + + prev_e = max_e # take + ret += 1 + if prev_e >= T: + break + + return ret if prev_e >= T else -1 + + def videoStitching_error(self, clips: List[List[int]], T: int) -> int: + """ + gready take the max coverage? + """ + A = [(s, -e, s, e) for s, e in clips] + A.sort() + ret = 1 + _, _, prev_s, prev_e = A[0] + if prev_s > 0: + return False + + for _, _, s, e in A[1:]: + if s <= prev_e and e > prev_e: + prev_e = e + ret += 1 + + +if __name__ == "__main__": + assert Solution().videoStitching([[0,4],[2,8]], 5) == 2 From f5979b4417321c1420b859a3880c1fae144d2a5d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 20 Apr 2019 13:07:50 -0700 Subject: [PATCH 267/344] 1021 --- 1021 Remove Outermost Parentheses.py | 96 ++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 1021 Remove Outermost Parentheses.py diff --git a/1021 Remove Outermost Parentheses.py b/1021 Remove Outermost Parentheses.py new file mode 100644 index 0000000..2609aa7 --- /dev/null +++ b/1021 Remove Outermost Parentheses.py @@ -0,0 +1,96 @@ +#!/usr/bin/python3 +""" +A valid parentheses string is either empty (""), "(" + A + ")", or A + B, where +A and B are valid parentheses strings, and + represents string concatenation. +For example, "", "()", "(())()", and "(()(()))" are all valid parentheses strings. + +A valid parentheses string S is primitive if it is nonempty, and there does not +exist a way to split it into S = A+B, with A and B nonempty valid parentheses +strings. + +Given a valid parentheses string S, consider its primitive decomposition: +S = P_1 + P_2 + ... + P_k, where P_i are primitive valid parentheses strings. + +Return S after removing the outermost parentheses of every primitive string in +the primitive decomposition of S. + +Example 1: +Input: "(()())(())" +Output: "()()()" +Explanation: +The input string is "(()())(())", with primitive decomposition "(()())" + "(())". +After removing outer parentheses of each part, this is "()()" + "()" = "()()()". + +Example 2: +Input: "(()())(())(()(()))" +Output: "()()()()(())" +Explanation: +The input string is "(()())(())(()(()))", with primitive decomposition +"(()())" + "(())" + "(()(()))". +After removing outer parentheses of each part, this is "()()" + "()" + +"()(())" = "()()()()(())". + +Example 3: +Input: "()()" +Output: "" +Explanation: +The input string is "()()", with primitive decomposition "()" + "()". +After removing outer parentheses of each part, this is "" + "" = "". + + +Note: +S.length <= 10000 +S[i] is "(" or ")" +S is a valid parentheses string +""" +from collections import deque + + +class Solution: + def removeOuterParentheses(self, S: str) -> str: + """ + Primitive parentheses will have equal number of opened and closed + parentheses. + + Use count + Exclude the first and last parathesis + """ + ret = [] + cnt = 0 + for e in S: + if e == "(": + cnt += 1 + if cnt > 1: + ret.append(e) + else: + cnt -= 1 + if cnt > 0: + ret.append(e) + + return "".join(ret) + + + def removeOuterParentheses_error(self, S: str) -> str: + """ + stack + deque + """ + ret = [] + stk = [] + cur_q = deque() + for e in S: + if e == "(": + stk.append(e) + else: + prev = stk.pop() + if stk: + cur_q.appendleft(prev) + cur_q.append(e) + else: + ret.extend(cur_q) + cur_q = deque() + + return "".join(ret) + + +if __name__ == "__main__": + assert Solution().removeOuterParentheses("(()())(())(()(()))") == "()()()()(())" From 88bba682c8d5132285a24abc012048627da62568 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 20 Apr 2019 22:27:39 -0700 Subject: [PATCH 268/344] 935 --- 935 Knight Dialer.py | 152 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 935 Knight Dialer.py diff --git a/935 Knight Dialer.py b/935 Knight Dialer.py new file mode 100644 index 0000000..0cada02 --- /dev/null +++ b/935 Knight Dialer.py @@ -0,0 +1,152 @@ +#!/usr/bin/python3 +""" +A chess knight can move as indicated in the chess diagram below: + +This time, we place our chess knight on any numbered key of a phone pad +(indicated above), and the knight makes N-1 hops. Each hop must be from one key +to another numbered key. + +Each time it lands on a key (including the initial placement of the knight), it +presses the number of that key, pressing N digits total. + +How many distinct numbers can you dial in this manner? + +Since the answer may be large, output the answer modulo 10^9 + 7. + + +Example 1: +Input: 1 +Output: 10 + +Example 2: +Input: 2 +Output: 20 + +Example 3: +Input: 3 +Output: 46 + +Note: +1 <= N <= 5000 +""" + + +MOD = 10 ** 9 + 7 + + +dirs = [ + (-2, 1), + (-1, 2), + (1, 2), + (2, 1), + (2, -1), + (1, -2), + (-1, -2), + (-2, -1), +] + +nbrs = { + 1: (6, 8), + 2: (7, 9), + 3: (4, 8), + 4: (3, 9, 0), + 5: tuple(), + 6: (1, 7, 0), + 7: (2, 6), + 8: (1, 3), + 9: (2, 4), + 0: (4, 6), +} + + +from collections import defaultdict + + +class Solution: + def knightDialer(self, N: int) -> int: + """ + DP + F[pos][step] = sum(F[nbr][step+1] for all nbr) + """ + F = defaultdict(lambda: defaultdict(int)) + for pos in range(10): + F[pos][N-1] = 1 + + for n in range(N-2, -1, -1): + for pos in range(10): + for nbr in nbrs[pos]: + F[pos][n] += F[nbr][n+1] + F[pos][n] %= MOD + + ret = 0 + for i in range(10): + ret += F[i][0] + ret %= MOD + + return ret + + +class SolutionTLE2: + def __init__(self): + self.cache = {} + + def knightDialer(self, N: int) -> int: + ret = 0 + for i in range(10): + ret += self.dfs(i, N-1) + ret %= MOD + + return ret + + def dfs(self, i, r): + if (i, r) not in self.cache: + ret = 0 + if r == 0: + ret = 1 + else: + for nbr in nbrs[i]: + ret += self.dfs(nbr, r-1) + + self.cache[i, r] = ret + + return self.cache[i, r] + + +class SolutionTLE: + def __init__(self): + # row, col size + self.m = 4 + self.n = 3 + self.cache = {} + + def knightDialer(self, N: int) -> int: + ret = 0 + for i in range(self.m): + for j in range(self.n): + if (i, j) != (3, 0) and (i, j) != (3, 2): + ret += self.dfs(i, j, N-1) + ret %= MOD + return ret + + def dfs(self, i, j, r): + if (i, j, r) not in self.cache: + ret = 0 + if r == 0: + ret = 1 + else: + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < self.m and 0 <= J < self.n and (I, J) != (3, 0) and (I, J) != (3, 2): + ret += self.dfs(I, J, r - 1) + ret %= MOD + + self.cache[i, j, r] = ret + + return self.cache[i, j, r] + + +if __name__ == "__main__": + assert Solution().knightDialer(1) == 10 + assert Solution().knightDialer(2) == 20 + assert Solution().knightDialer(3) == 46 From 27f86a0794f36df2293f3a3a7e6aab35842fdfdb Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 21 Apr 2019 19:49:51 -0700 Subject: [PATCH 269/344] 917 --- 917 Reverse Only Letters.py | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 917 Reverse Only Letters.py diff --git a/917 Reverse Only Letters.py b/917 Reverse Only Letters.py new file mode 100644 index 0000000..210fc41 --- /dev/null +++ b/917 Reverse Only Letters.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +""" +Given a string S, return the "reversed" string where all characters that are not +a letter stay in the same place, and all letters reverse their positions. + +Example 1: + +Input: "ab-cd" +Output: "dc-ba" + +Example 2: +Input: "a-bC-dEf-ghIj" +Output: "j-Ih-gfE-dCba" + +Example 3: +Input: "Test1ng-Leet=code-Q!" +Output: "Qedo1ct-eeLg=ntse-T!" + + +Note: +S.length <= 100 +33 <= S[i].ASCIIcode <= 122 +S doesn't contain \ or " +""" + + +class Solution: + def reverseOnlyLetters(self, S: str) -> str: + lst = list(S) + i = 0 + n = len(lst) + j = n - 1 + while True: + while i < n and not lst[i].isalpha(): + i += 1 + while j >= 0 and not lst[j].isalpha(): + j -= 1 + + if i < j and i < n and j >= 0: + lst[i], lst[j] = lst[j], lst[i] + i += 1 + j -= 1 + else: + break + + return "".join(lst) + + +if __name__ == "__main__": + assert Solution().reverseOnlyLetters("Test1ng-Leet=code-Q!") == "Qedo1ct-eeLg=ntse-T!" From 4816a47a44d005bc10ee9288acdaa59d3ab4de62 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 21 Apr 2019 22:00:06 -0700 Subject: [PATCH 270/344] 923 --- 923 3Sum With Multiplicity.py | 123 ++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 923 3Sum With Multiplicity.py diff --git a/923 3Sum With Multiplicity.py b/923 3Sum With Multiplicity.py new file mode 100644 index 0000000..32a6207 --- /dev/null +++ b/923 3Sum With Multiplicity.py @@ -0,0 +1,123 @@ +#!/usr/bin/python3 +""" +Given an integer array A, and an integer target, return the number of tuples +i, j, k such that i < j < k and A[i] + A[j] + A[k] == target. + +As the answer can be very large, return it modulo 10^9 + 7. + +Example 1: + +Input: A = [1,1,2,2,3,3,4,4,5,5], target = 8 +Output: 20 +Explanation: +Enumerating by the values (A[i], A[j], A[k]): +(1, 2, 5) occurs 8 times; +(1, 3, 4) occurs 8 times; +(2, 2, 4) occurs 2 times; +(2, 3, 3) occurs 2 times. +Example 2: + +Input: A = [1,1,2,2,2,2], target = 5 +Output: 12 +Explanation: +A[i] = 1, A[j] = A[k] = 2 occurs 12 times: +We choose one 1 from [1,1] in 2 ways, +and two 2s from [2,2,2,2] in 6 ways. + + +Note: + +3 <= A.length <= 3000 +0 <= A[i] <= 100 +0 <= target <= 300 +""" +from typing import List +from collections import defaultdict + + +MOD = 10 ** 9 + 7 + + +class Solution: + def threeSumMulti(self, A: List[int], target: int) -> int: + """ + Adapted from 3 sum + 3 pointers O(N^2) + j, k scan each element once + """ + A.sort() + n = len(A) + ret = 0 + for i in range(n): + j = i + 1 + k = n - 1 + while j < k: + if A[j] + A[k] < target - A[i]: + j += 1 + elif A[j] + A[k] > target - A[i]: + k -= 1 + else: # equal + l_cnt = 1 + while j + l_cnt < n and A[j + l_cnt] == A[j]: + l_cnt += 1 + + r_cnt = 1 + while k - r_cnt >= 0 and A[k - r_cnt] == A[k]: + r_cnt += 1 + + if A[j] != A[k]: + ret += l_cnt * r_cnt + ret %= MOD + else: + ret += l_cnt * (l_cnt - 1) // 2 # nC2 + ret %= MOD + + j += l_cnt + k -= r_cnt + + return ret + + def threeSumMulti_TLE(self, A: List[int], target: int) -> int: + """ + O(n * target * 3) + Let F[i][t][k] be the number of k sums using A[:i] to target t + """ + n = len(A) + F = [[[0 for _ in range(3 + 1)] for _ in range(target + 1)] for _ in range(n+1)] + + for i in range(n+1): + F[i][0][0] = 1 + + for i in range(1, n + 1): + for t in range(target + 1): + for k in range(1, 3 + 1): + # choose A[i-1] or not + F[i][t][k] = F[i-1][t][k] % MOD + if t - A[i-1] >= 0: + F[i][t][k] += F[i-1][t-A[i-1]][k-1] % MOD + + print(F[n][target][3]) + return F[n][target][3] + + def threeSumMulti_TLE(self, A: List[int], target: int) -> int: + """ + O(n * target * 3) + Let F[i][t][k] be the number of k sums using A[:i] to target t + """ + F = defaultdict(lambda: defaultdict(lambda: defaultdict(int))) + n = len(A) + for i in range(n+1): + F[i][0][0] = 1 + + for i in range(1, n + 1): + for t in range(target + 1): + for k in range(1, 3 + 1): + # choose A[i-1] or not + F[i][t][k] = F[i-1][t][k] + F[i-1][t-A[i-1]][k-1] + F[i][t][k] %= MOD + + return F[n][target][3] + + +if __name__ == "__main__": + assert Solution().threeSumMulti([1,1,2,2,3,3,4,4,5,5], 8) == 20 From eeba6e0339d223f0bde18d5f0a75f79e676d5ab7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 21 Apr 2019 22:19:27 -0700 Subject: [PATCH 271/344] 923 --- 923 3Sum With Multiplicity.py | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/923 3Sum With Multiplicity.py b/923 3Sum With Multiplicity.py index 32a6207..96ae4f2 100644 --- a/923 3Sum With Multiplicity.py +++ b/923 3Sum With Multiplicity.py @@ -40,6 +40,49 @@ class Solution: def threeSumMulti(self, A: List[int], target: int) -> int: + """ + Adapted from 3 sum + 3 pointers O(N + K^2) + j, k scan each element once + """ + counter = defaultdict(int) + for a in A: + counter[a] += 1 + + keys = list(counter.keys()) + keys.sort() + n = len(keys) + ret = 0 + for i in range(n): + j = i # not i + 1 + k = n - 1 + while j <= k: # not < + a, b, c = keys[i], keys[j], keys[k] + if b + c < target - a: + j += 1 + elif b + c > target - a: + k -= 1 + else: # equal + if a < b < c: + ret += counter[a] * counter[b] * counter[c] + elif a == b < c: + # nC2 + ret += counter[a] * (counter[a] - 1) // 2 * counter[c] + elif a < b == c: + ret += counter[a] * counter[b] * (counter[b] - 1) // 2 + elif a== b == c: + # nC3 + ret += counter[a] * (counter[a] - 1) * (counter[a] - 2) // (3 * 2) + else: + raise + + ret %= MOD + j += 1 + k -= 1 + + return ret + + def threeSumMulti_TLE(self, A: List[int], target: int) -> int: """ Adapted from 3 sum 3 pointers O(N^2) From 40597a8bb5f95835e6ae3800fe823fdbd45b63c9 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 23 Apr 2019 22:37:01 -0700 Subject: [PATCH 272/344] 1029 --- 1029 Two City Scheduling.py | 76 +++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 1029 Two City Scheduling.py diff --git a/1029 Two City Scheduling.py b/1029 Two City Scheduling.py new file mode 100644 index 0000000..ac6fc76 --- /dev/null +++ b/1029 Two City Scheduling.py @@ -0,0 +1,76 @@ +#!/usr/bin/python3 +""" +There are 2N people a company is planning to interview. The cost of flying the +i-th person to city A is costs[i][0], and the cost of flying the i-th person to +city B is costs[i][1]. + +Return the minimum cost to fly every person to a city such that exactly N people +arrive in each city. + + + +Example 1: + +Input: [[10,20],[30,200],[400,50],[30,20]] +Output: 110 +Explanation: +The first person goes to city A for a cost of 10. +The second person goes to city A for a cost of 30. +The third person goes to city B for a cost of 50. +The fourth person goes to city B for a cost of 20. + +The total minimum cost is 10 + 30 + 50 + 20 = 110 to have half the people +interviewing in each city. + +Note: + +1 <= costs.length <= 100 +It is guaranteed that costs.length is even. +1 <= costs[i][0], costs[i][1] <= 1000 +""" + + +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + """ + sort by city A and greedy? [30, 20]? + sort by total? + sort by diff - either choose A or B, the difference matters + + a - b: incremental cost of flying A instead of B + """ + A = [(a - b, a, b) for a, b in costs] + A.sort() + ret = 0 + remain = len(A) // 2 + for _, a, b in A: + if remain > 0: + ret += a + remain -= 1 + else: + ret += b + + return ret + + def twoCitySchedCost_error(self, costs: List[List[int]]) -> int: + """ + sort by city A and greedy? [30, 20]? + sort by total? + sort by diff - either choose A or B, the difference matters + + Error in the abs of difference + """ + A = [(abs(a - b), a, b) for a, b in costs] + A.sort(reverse=True) + ret = 0 + remain = len(A) // 2 + for _, a, b in A: + if a > b: + ret += b + elif remain > 0: + ret += a + remain -= 1 + else: + ret += b + + return ret From 1a6584deafd26390eea91eccbd1007f7eedef204 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 23 Apr 2019 22:45:57 -0700 Subject: [PATCH 273/344] 1030 --- 1030 Matrix Cells in Distance Order.py | 56 ++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 1030 Matrix Cells in Distance Order.py diff --git a/1030 Matrix Cells in Distance Order.py b/1030 Matrix Cells in Distance Order.py new file mode 100644 index 0000000..d590078 --- /dev/null +++ b/1030 Matrix Cells in Distance Order.py @@ -0,0 +1,56 @@ +#!/usr/bin/python3 +""" +We are given a matrix with R rows and C columns has cells with integer +coordinates (r, c), where 0 <= r < R and 0 <= c < C. + +Additionally, we are given a cell in that matrix with coordinates (r0, c0). + +Return the coordinates of all cells in the matrix, sorted by their distance from +(r0, c0) from smallest distance to largest distance. Here, the distance between +two cells (r1, c1) and (r2, c2) is the Manhattan distance, |r1 - r2| + |c1 - c2|. +(You may return the answer in any order that satisfies this condition.) + +Example 1: +Input: R = 1, C = 2, r0 = 0, c0 = 0 +Output: [[0,0],[0,1]] +Explanation: The distances from (r0, c0) to other cells are: [0,1] + +Example 2: +Input: R = 2, C = 2, r0 = 0, c0 = 1 +Output: [[0,1],[0,0],[1,1],[1,0]] +Explanation: The distances from (r0, c0) to other cells are: [0,1,1,2] +The answer [[0,1],[1,1],[0,0],[1,0]] would also be accepted as correct. + +Example 3: +Input: R = 2, C = 3, r0 = 1, c0 = 2 +Output: [[1,2],[0,2],[1,1],[0,1],[1,0],[0,0]] +Explanation: The distances from (r0, c0) to other cells are: [0,1,1,2,2,3] +There are other answers that would also be accepted as correct, such as [[1,2],[1,1],[0,2],[1,0],[0,1],[0,0]]. + +Note: + +1 <= R <= 100 +1 <= C <= 100 +0 <= r0 < R +0 <= c0 < C +""" +from typing import List + + +class Solution: + def allCellsDistOrder(self, R: int, C: int, r0: int, c0: int) -> List[List[int]]: + """ + bucket sort + """ + r_max = max(r0, R-1 - r0) + c_max = max(c0, C-1 - c0) + lst = [[] for _ in range(r_max + c_max + 1)] + for i in range(R): + for j in range(C): + lst[abs(i - r0) + abs(j - c0)].append([i, j]) + + ret = [] + for e in lst: + ret.extend(e) + + return ret From 46971fcfed60d30fb5af927019407ed14cc78ab1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 23 Apr 2019 23:18:16 -0700 Subject: [PATCH 274/344] 1031 --- ...um Sum of Two Non-Overlapping Subarrays.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 1031 Maximum Sum of Two Non-Overlapping Subarrays.py diff --git a/1031 Maximum Sum of Two Non-Overlapping Subarrays.py b/1031 Maximum Sum of Two Non-Overlapping Subarrays.py new file mode 100644 index 0000000..7cd976f --- /dev/null +++ b/1031 Maximum Sum of Two Non-Overlapping Subarrays.py @@ -0,0 +1,64 @@ +#!/usr/bin/python3 +""" +Given an array A of non-negative integers, return the maximum sum of elements in +two non-overlapping (contiguous) subarrays, which have lengths L and M. (For +clarification, the L-length subarray could occur before or after the M-length +subarray.) + +Formally, return the largest V for which V = (A[i] + A[i+1] + ... + A[i+L-1]) + +(A[j] + A[j+1] + ... + A[j+M-1]) and either: + +0 <= i < i + L - 1 < j < j + M - 1 < A.length, or +0 <= j < j + M - 1 < i < i + L - 1 < A.length. + + +Example 1: +Input: A = [0,6,5,2,2,5,1,9,4], L = 1, M = 2 +Output: 20 +Explanation: One choice of subarrays is [9] with length 1, and [6,5] with length +2. + +Example 2: +Input: A = [3,8,1,3,2,1,8,9,0], L = 3, M = 2 +Output: 29 +Explanation: One choice of subarrays is [3,8,1] with length 3, and [8,9] with +length 2. + +Example 3: +Input: A = [2,1,5,6,0,9,5,0,3,8], L = 4, M = 3 +Output: 31 +Explanation: One choice of subarrays is [5,6,0,9] with length 4, and [3,8] with +length 3. + +Note: +L >= 1 +M >= 1 +L + M <= A.length <= 1000 +0 <= A[i] <= 1000 +""" +from typing import List + + +class Solution: + def maxSumTwoNoOverlap(self, A: List[int], L: int, M: int) -> int: + """ + Prefix sum + Brute force O(N^2) + two pointer i, j + """ + n = len(A) + F = [0 for _ in range(n + 1)] + for i, a in enumerate(A): + F[i+1] = F[i] + a + + ret = -float("inf") + for l, m in ((L, M), (M, L)): + for i in range(n + 1 - l): + for j in range(i + l, n + 1 - m): # upper needs +1 here + cur = F[i + l] - F[i] + F[j + m] - F[j] + ret = max(ret, cur) + + return ret + + +if __name__ == "__main__": + assert Solution().maxSumTwoNoOverlap([0,6,5,2,2,5,1,9,4], 1, 2) == 20 From 070bcbe251a833e4e146faf8d569f5d58c475e4b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 24 Apr 2019 00:12:27 -0700 Subject: [PATCH 275/344] 741 --- 741 Cherry Pickup.py | 97 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 741 Cherry Pickup.py diff --git a/741 Cherry Pickup.py b/741 Cherry Pickup.py new file mode 100644 index 0000000..bff2a84 --- /dev/null +++ b/741 Cherry Pickup.py @@ -0,0 +1,97 @@ +#!/usr/bin/python3 +""" +In a N x N grid representing a field of cherries, each cell is one of three +possible integers. + +0 means the cell is empty, so you can pass through; +1 means the cell contains a cherry, that you can pick up and pass through; +-1 means the cell contains a thorn that blocks your way. + +Your task is to collect maximum number of cherries possible by following the +rules below: + +Starting at the position (0, 0) and reaching (N-1, N-1) by moving right or down +through valid path cells (cells with value 0 or 1); +After reaching (N-1, N-1), returning to (0, 0) by moving left or up through +valid path cells; +When passing through a path cell containing a cherry, you pick it up and the +cell becomes an empty cell (0); +If there is no valid path between (0, 0) and (N-1, N-1), then no cherries can be +collected. + +Example 1: +Input: grid = +[[0, 1, -1], + [1, 0, -1], + [1, 1, 1]] +Output: 5 +Explanation: +The player started at (0, 0) and went down, down, right right to reach (2, 2). +4 cherries were picked up during this single trip, and the matrix becomes +[[0,1,-1],[0,0,-1],[0,0,0]]. +Then, the player went left, up, up, left to return home, picking up one more +cherry. +The total number of cherries picked up is 5, and this is the maximum possible. + +Note: +grid is an N by N 2D array, with 1 <= N <= 50. +Each grid[i][j] is an integer in the set {-1, 0, 1}. +It is guaranteed that grid[0][0] and grid[N-1][N-1] are not -1. +""" +from typing import List + + +class Solution: + def __init__(self): + self.cache = {} + + def cherryPickup(self, grid: List[List[int]]) -> int: + """ + DP go and back + Go back probably related - yes it is related + + Instead of walking from end to beginning, let's reverse the second leg + of the path, so we are only considering two paths from the beginning to + the end. + """ + return max(0, self.F(grid, 0, 0, 0)) + + def F(self, grid, r1, c1, r2): + n = len(grid) + if (r1, c1, r2) not in self.cache: + ret = float("-inf") + c2 = r1 + c1 - r2 # r1 + c1 == r2 + c2 + if 0 <= r1 < n and 0 <= c1 < n and 0 <= r2 < n and 0 <= c2 < n: + if grid[r1][c1] != -1 and grid[r2][c2] != -1: + ret = 0 + ret += grid[r1][c1] + if r1 != r2: + ret += grid[r2][c2] + + if r1 == n - 1 and c1 == n - 1: + pass # seed, otherwise -inf + else: + ret += max( + self.F(grid, r1+1, c1, r2+1), # down, down + self.F(grid, r1+1, c1, r2), # down, right + self.F(grid, r1, c1+1, r2+1), # right, down + self.F(grid, r1, c1+1, r2), # right, right + ) + + self.cache[r1, c1, r2] = ret + + return self.cache[r1, c1, r2] + + +if __name__ == "__main__": + assert Solution().cherryPickup( + [[0, 1, -1], + [1, 0, -1], + [1, 1, 1]] + ) == 5 + + assert Solution().cherryPickup( + [[1, 1, -1], + [1, -1, 1], + [-1, 1, 1]] + ) == 0 From 900c1de8a35f3bbd8c161cc3ead15713978c926e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 24 Apr 2019 14:56:31 -0700 Subject: [PATCH 276/344] 460 --- 460 LFU Cache.py | 96 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 460 LFU Cache.py diff --git a/460 LFU Cache.py b/460 LFU Cache.py new file mode 100644 index 0000000..02bca02 --- /dev/null +++ b/460 LFU Cache.py @@ -0,0 +1,96 @@ +#!/usr/bin/python3 +""" +Design and implement a data structure for Least Frequently Used (LFU) cache. It +should support the following operations: get and put. + +get(key) - Get the value (will always be positive) of the key if the key exists +in the cache, otherwise return -1. +put(key, value) - Set or insert the value if the key is not already present. +When the cache reaches its capacity, it should invalidate the least frequently +used item before inserting a new item. For the purpose of this problem, when +there is a tie (i.e., two or more keys that have the same frequency), the least +recently used key would be evicted. + +Follow up: +Could you do both operations in O(1) time complexity? + +Example: + +LFUCache cache = new LFUCache( 2 /* capacity */ ); + +cache.put(1, 1); +cache.put(2, 2); +cache.get(1); // returns 1 +cache.put(3, 3); // evicts key 2 +cache.get(2); // returns -1 (not found) +cache.get(3); // returns 3. +cache.put(4, 4); // evicts key 1. +cache.get(1); // returns -1 (not found) +cache.get(3); // returns 3 +cache.get(4); // returns 4 +""" +from collections import defaultdict, OrderedDict +DUMMY = None + + +class LFUCache: + + def __init__(self, capacity: int): + """ + Need priority queue (pq) to keep contract of frequency + + LRU: doubly linked list and map + Sift up and sift down? + + Ordereded Dict + map: key -> value + map: key -> frequency + map: frequency -> OrderededDict[keys] + + min count is +1 + """ + self.cap = capacity + self.values = {} + self.freqs = defaultdict(int) + self.keys = defaultdict(OrderedDict) + self.mini = -1 # mini frequency + + def get(self, key: int) -> int: + if key in self.values: + val = self.values[key] + freq_org = self.freqs[key] + self.freqs[key] += 1 + del self.keys[freq_org][key] + self.keys[freq_org + 1][key] = DUMMY # dummy + + if freq_org == self.mini and len(self.keys[self.mini]) == 0: + self.mini = freq_org + 1 + + return val + else: + return - 1 + + def put(self, key: int, value: int) -> None: + if self.cap == 0: # trivial + return + + if key in self.values: + self.values[key] = value + self.get(key) # update + else: + if len(self.values) >= self.cap: + evit_key, _ = self.keys[self.mini].popitem(last=False) # least recent is at head + del self.values[evit_key] + del self.freqs[evit_key] + + self.values[key] = value + self.freqs[key] = 0 + self.keys[0][key] = DUMMY + self.get(key) # update + self.mini = 1 + + +# Your LFUCache object will be instantiated and called as such: +# obj = LFUCache(capacity) +# param_1 = obj.get(key) +# obj.put(key,value) From c6b1cf7eccc478b0d4e9c9851bc7441ceb31dbab Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 24 Apr 2019 15:21:18 -0700 Subject: [PATCH 277/344] 520 --- 520 Detect Capital.py | 48 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 520 Detect Capital.py diff --git a/520 Detect Capital.py b/520 Detect Capital.py new file mode 100644 index 0000000..59f5077 --- /dev/null +++ b/520 Detect Capital.py @@ -0,0 +1,48 @@ +#!/usr/bin/python3 +""" +Given a word, you need to judge whether the usage of capitals in it is right or +not. + +We define the usage of capitals in a word to be right when one of the following +cases holds: + +All letters in this word are capitals, like "USA". +All letters in this word are not capitals, like "leetcode". +Only the first letter in this word is capital if it has more than one letter, +like "Google". +Otherwise, we define that this word doesn't use capitals in a right way. +Example 1: +Input: "USA" +Output: True +Example 2: +Input: "FlaG" +Output: False +Note: The input will be a non-empty word consisting of uppercase and lowercase +latin letters. +""" + + +class Solution: + def detectCapitalUse(self, word: str) -> bool: + """ + Two passes is easy + How to do it in one pass + """ + if not word: + return True + + head_upper = word[0].isupper() + + # except for the head + has_lower = False + has_upper = False + for w in word[1:]: + if w.isupper(): + has_upper = True + if has_lower or not head_upper: + return False + else: + has_lower = True + if has_upper: + return False + return True From 9afb441a1450a842e0a740c4783790865b8fe813 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 24 Apr 2019 16:01:24 -0700 Subject: [PATCH 278/344] 1028 --- ... Recover a Tree From Preorder Traversal.py | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 1028 Recover a Tree From Preorder Traversal.py diff --git a/1028 Recover a Tree From Preorder Traversal.py b/1028 Recover a Tree From Preorder Traversal.py new file mode 100644 index 0000000..4fc6c13 --- /dev/null +++ b/1028 Recover a Tree From Preorder Traversal.py @@ -0,0 +1,143 @@ +#!/usr/bin/python3 +""" +We run a preorder depth first search on the root of a binary tree. + +At each node in this traversal, we output D dashes (where D is the depth of this +node), then we output the value of this node. (If the depth of a node is D, the +depth of its immediate child is D+1. The depth of the root node is 0.) + +If a node has only one child, that child is guaranteed to be the left child. + +Given the output S of this traversal, recover the tree and return its root. + + +Example 1: +Input: "1-2--3--4-5--6--7" +Output: [1,2,5,3,4,6,7] + +Example 2: +Input: "1-2--3---4-5--6---7" +Output: [1,2,5,3,null,6,null,4,null,7] + + +Example 3: +Input: "1-401--349---90--88" +Output: [1,401,null,349,88,90] + + +Note: +The number of nodes in the original tree is between 1 and 1000. +Each node will have a value between 1 and 10^9. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +from collections import OrderedDict + + +class Solution: + def recoverFromPreorder(self, S: str) -> TreeNode: + """ + map: node -> depth + stack of pi (incompleted) + """ + depth = 0 + # parse + n = len(S) + i = 0 + root = None + stk = [] + while i < n: + if S[i] == "-": + depth += 1 + i += 1 + else: + j = i + while j < n and S[j] != "-": + j += 1 + + val = int(S[i:j]) + + # construct + cur = TreeNode(val) + if depth == 0: + root = cur + stk = [(depth, root)] + else: + assert stk + while stk[-1][0] != depth - 1: + stk.pop() + + _, pi = stk[-1] + if not pi.left: + pi.left = cur + elif not pi.right: + pi.right = cur + stk.pop() + else: + raise + stk.append((depth, cur)) + + depth = 0 + i = j + + return root + + def recoverFromPreorder_error(self, S: str) -> TreeNode: + """ + map: node -> depth + stack of pi (incompleted) + """ + depth = 0 + depths = OrderedDict() + # parse + n = len(S) + i = 0 + while i < n: + if S[i] == "-": + depth += 1 + i += 1 + else: + j = i + while j < n and S[j] != "-": + j += 1 + + val = int(S[i:j]) + depths[val] = depth + depth = 0 + i = j + + # construct + stk = [] + root = None + for k, v in depths.items(): + cur = TreeNode(k) + if v == 0: + root = cur + stk = [root] + else: + assert stk + while depths[stk[-1].val] != v - 1: + stk.pop() + + if not stk[-1].left: + stk[-1].left = cur + elif not stk[-1].right: + stk[-1].right = cur + stk.pop() + else: + raise + stk.append(cur) + + return root + + +if __name__ == "__main__": + assert Solution().recoverFromPreorder("5-4--4") + assert Solution().recoverFromPreorder("1-2--3--4-5--6--7") From d128a5d887dca700eb57197a7bc482b7d27d5c2b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 24 Apr 2019 17:56:29 -0700 Subject: [PATCH 279/344] 502 --- 502 IPO.py | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 502 IPO.py diff --git a/502 IPO.py b/502 IPO.py new file mode 100644 index 0000000..f8dcf67 --- /dev/null +++ b/502 IPO.py @@ -0,0 +1,94 @@ +#!/usr/bin/python3 +""" +Suppose LeetCode will start its IPO soon. In order to sell a good price of its +shares to Venture Capital, LeetCode would like to work on some projects to + increase its capital before the IPO. Since it has limited resources, it can + only finish at most k distinct projects before the IPO. Help LeetCode design + the best way to maximize its total capital after finishing at most k distinct + projects. + +You are given several projects. For each project i, it has a pure profit Pi and +a minimum capital of Ci is needed to start the corresponding project. +Initially, you have W capital. When you finish a project, you will obtain its +pure profit and the profit will be added to your total capital. + +To sum up, pick a list of at most k distinct projects from given projects to +maximize your final capital, and output your final maximized capital. + +Example 1: +Input: k=2, W=0, Profits=[1,2,3], Capital=[0,1,1]. + +Output: 4 + +Explanation: Since your initial capital is 0, you can only start the project indexed 0. + After finishing it you will obtain profit 1 and your capital becomes 1. + With capital 1, you can either start the project indexed 1 or the project indexed 2. + Since you can choose at most 2 projects, you need to finish the project indexed 2 to get the maximum capital. + Therefore, output the final maximized capital, which is 0 + 1 + 3 = 4. +Note: +You may assume all numbers in the input are non-negative integers. +The length of Profits array and Capital array will not exceed 50,000. +The answer is guaranteed to fit in a 32-bit signed integer. +""" +from typing import List +import heapq + + +class Solution: + def findMaximizedCapital(self, k: int, W: int, Profits: List[int], Capital: List[int]) -> int: + """ + Greedy + dual PQ + Greedy: need max profit meeting the current capital requirement + 1st pq sort by min capital + 2nd pq sort by max profit + + O(N logN) + O(N log N) + """ + capital_q = list(zip(Capital, Profits)) + profit_q = [] + heapq.heapify(capital_q) + capital = W + for _ in range(k): + while capital_q and capital_q[0][0] <= capital: + _, pro = heapq.heappop(capital_q) + heapq.heappush(profit_q, (-pro, pro)) + + if profit_q: + _, pro = heapq.heappop(profit_q) + capital += pro + else: + break + + return capital + + def findMaximizedCapital_TLE(self, k: int, W: int, Profits: List[int], Capital: List[int]) -> int: + """ + Knapsack problem + + Difference from original knapsack: weight vs. capitcal + profit + Doing a project has profit and open new project opportunities. + + F[m][c] = F[m-1][] + profit[i] + final F[k][W] + + Greedy, always do the max profits given the capital requirement fullfilled + + O(k * N) + """ + capital = W + n = len(Profits) + visited = [False for _ in range(n)] + for _ in range(k): + maxa = 0 + maxa_i = 0 + for i in range(n): + if not visited[i] and Profits[i] >= maxa and Capital[i] <= capital: + maxa = Profits[i] + maxa_i = i + if maxa > 0: + capital += maxa + visited[maxa_i] = True + else: + break + + return capital From 3777f261b9dacb3fb66813d1d6da33aaee6d9304 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 24 Apr 2019 18:34:07 -0700 Subject: [PATCH 280/344] 606 --- 606 Construct String from Binary Tree.py | 58 ++++++++++++++++++++++++ 875 Koko Eating Bananas.py | 5 +- 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 606 Construct String from Binary Tree.py diff --git a/606 Construct String from Binary Tree.py b/606 Construct String from Binary Tree.py new file mode 100644 index 0000000..d843c90 --- /dev/null +++ b/606 Construct String from Binary Tree.py @@ -0,0 +1,58 @@ +#!/usr/bin/python3 +""" +You need to construct a string consists of parenthesis and integers from a +binary tree with the preorder traversing way. + +The null node needs to be represented by empty parenthesis pair "()". And you +need to omit all the empty parenthesis pairs that don't affect the one-to-one mapping relationship between the string and the original binary tree. + +Example 1: +Input: Binary tree: [1,2,3,4] + 1 + / \ + 2 3 + / + 4 + +Output: "1(2(4))(3)" + +Explanation: Originallay it needs to be "1(2(4)())(3()())", +but you need to omit all the unnecessary empty parenthesis pairs. +And it will be "1(2(4))(3)". +Example 2: +Input: Binary tree: [1,2,3,null,4] + 1 + / \ + 2 3 + \ + 4 + +Output: "1(2()(4))(3)" + +Explanation: Almost the same as the first example, +except we can't omit the first parenthesis pair to break the one-to-one mapping +relationship between the input and the output. +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def tree2str(self, t: TreeNode) -> str: + if not t: + return "" + + left = self.tree2str(t.left) + right = self.tree2str(t.right) + ret = [str(t.val)] + if left or right: + ret.append("(" + left + ")") + if right: + ret.append("(" + right + ")") + + return "".join(ret) diff --git a/875 Koko Eating Bananas.py b/875 Koko Eating Bananas.py index 91fae08..74a0eff 100644 --- a/875 Koko Eating Bananas.py +++ b/875 Koko Eating Bananas.py @@ -57,7 +57,10 @@ def minEatingSpeed(self, piles: List[int], H: int) -> int: lo = 1 while lo < hi: mid = (lo + hi) // 2 - if sum(math.ceil(piles[i] / mid) for i in range(n)) > H: + if sum( + math.ceil(piles[i] / mid) + for i in range(n) + ) > H: lo = mid + 1 else: hi = mid From 72da29a8de3d39514f8819067793cd697e0bc7f7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 24 Apr 2019 18:55:32 -0700 Subject: [PATCH 281/344] 622 --- 622 Design Circular Queue.py | 98 ++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 622 Design Circular Queue.py diff --git a/622 Design Circular Queue.py b/622 Design Circular Queue.py new file mode 100644 index 0000000..a376971 --- /dev/null +++ b/622 Design Circular Queue.py @@ -0,0 +1,98 @@ +#!/usr/bin/python3 +""" +Design your implementation of the circular queue. The circular queue is a +linear data structure in which the operations are performed based on FIFO (First +In First Out) principle and the last position is connected back to the first +position to make a circle. It is also called "Ring Buffer". + +One of the benefits of the circular queue is that we can make use of the spaces +in front of the queue. In a normal queue, once the queue becomes full, we cannot +insert the next element even if there is a space in front of the queue. But +using the circular queue, we can use the space to store new values. + +Your implementation should support following operations: + +MyCircularQueue(k): Constructor, set the size of the queue to be k. +Front: Get the front item from the queue. If the queue is empty, return -1. +Rear: Get the last item from the queue. If the queue is empty, return -1. +enQueue(value): Insert an element into the circular queue. Return true if the +operation is successful. +deQueue(): Delete an element from the circular queue. Return true if the +operation is successful. +isEmpty(): Checks whether the circular queue is empty or not. +isFull(): Checks whether the circular queue is full or not. +""" + + +class MyCircularQueue: + + def __init__(self, k: int): + """ + Initialize your data structure here. Set the size of the queue to be k. + """ + self.head = 0 + self.tail = -1 + self.sz = 0 + self.k = k + self.lst = [None for _ in range(k)] + + + def enQueue(self, value: int) -> bool: + """ + Insert an element into the circular queue. Return true if the operation is successful. + """ + if self.sz >= self.k: + return False + + self.tail += 1 + self.lst[self.tail % self.k] = value + self.sz += 1 + return True + + def deQueue(self) -> bool: + """ + Delete an element from the circular queue. Return true if the operation is successful. + """ + if self.sz <= 0: + return False + + self.lst[self.head % self.k] = None + self.head += 1 + self.sz -= 1 + return True + + def Front(self) -> int: + """ + Get the front item from the queue. + """ + ret = self.lst[self.head % self.k] + return ret if ret is not None else -1 + + def Rear(self) -> int: + """ + Get the last item from the queue. + """ + ret = self.lst[self.tail % self.k] + return ret if ret is not None else -1 + + def isEmpty(self) -> bool: + """ + Checks whether the circular queue is empty or not. + """ + return self.sz == 0 + + def isFull(self) -> bool: + """ + Checks whether the circular queue is full or not. + """ + return self.sz == self.k + + +# Your MyCircularQueue object will be instantiated and called as such: +# obj = MyCircularQueue(k) +# param_1 = obj.enQueue(value) +# param_2 = obj.deQueue() +# param_3 = obj.Front() +# param_4 = obj.Rear() +# param_5 = obj.isEmpty() +# param_6 = obj.isFull() From c6122b92ab0912161594a2e2bca205ce1de880d6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 25 Apr 2019 21:48:21 -0700 Subject: [PATCH 282/344] 439 --- 439 Ternary Expression Parser.py | 66 ++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 439 Ternary Expression Parser.py diff --git a/439 Ternary Expression Parser.py b/439 Ternary Expression Parser.py new file mode 100644 index 0000000..4f49bf1 --- /dev/null +++ b/439 Ternary Expression Parser.py @@ -0,0 +1,66 @@ +#!/usr/bin/python3 +""" +Premium question +""" + + +class Solution: + def parseTernary(self, expression: str) -> str: + """ + stk from right to left parsing, including the operand and operator + """ + stk = [] + for c in reversed(expression): + if stk and stk[-1] == "?": + stk.pop() # ? + first = stk.pop() + stk.pop() # : + second = stk.pop() + if c == "T": + stk.append(first) + else: + stk.append(second) + else: + stk.append(c) + + return stk[0] + + def parseTernary_complex(self, expression: str) -> str: + """ + tokenize + recursive (dfs)? + + stk from right to left, only include the operand + + can handle multiple digit (not required) + """ + n = len(expression) + stk = [] + i = n - 1 + while i >= 0: + j = i + while j >= 0 and expression[j] not in (":", "?"): + j -= 1 + + if j < i: + stk.append(expression[j+1:i+1]) + + if expression[j] == ":": + i = j - 1 + else: # "?" + i = j - 1 + if expression[i] == "T": + a = stk.pop() + stk.pop() + stk.append(a) + i -= 1 + else: + stk.pop() + i -= 1 + + return stk[0] + + + +if __name__ == "__main__": + assert Solution().parseTernary("F?1:T?4:5") == "4" + assert Solution().parseTernary("T?T?F:5:3") == "F" From d528f59dba55034e91023602ae3818868faa8f44 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 25 Apr 2019 21:57:09 -0700 Subject: [PATCH 283/344] 543 --- 543 Diameter of Binary Tree.py | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 543 Diameter of Binary Tree.py diff --git a/543 Diameter of Binary Tree.py b/543 Diameter of Binary Tree.py new file mode 100644 index 0000000..f452773 --- /dev/null +++ b/543 Diameter of Binary Tree.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +""" +Given a binary tree, you need to compute the length of the diameter of the tree. +The diameter of a binary tree is the length of the longest path between any two +nodes in a tree. This path may or may not pass through the root. + +Example: +Given a binary tree + 1 + / \ + 2 3 + / \ + 4 5 +Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3]. + +Note: The length of path between two nodes is represented by the number of edges +between them. +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + """ + dfs, return the longest path (#nodes) ended at the subroot/current node + """ + self.ret = 0 + + def diameterOfBinaryTree(self, root: TreeNode) -> int: + self.dfs(root) + return self.ret + + def dfs(self, node): + """ + return #nodes ended at node including itself + """ + if not node: + return 0 + + l = self.dfs(node.left) + r = self.dfs(node.right) + self.ret = max(self.ret, l + 1 + r - 1) # path length is the #nodes - 1 + return max(l, r) + 1 From f903ea63f202e8d6fee02ef47db2ac40efe00990 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 25 Apr 2019 22:07:46 -0700 Subject: [PATCH 284/344] 663 --- 663 Equal Tree Partition.py | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 663 Equal Tree Partition.py diff --git a/663 Equal Tree Partition.py b/663 Equal Tree Partition.py new file mode 100644 index 0000000..1ba8520 --- /dev/null +++ b/663 Equal Tree Partition.py @@ -0,0 +1,45 @@ +#!/usr/bin/python3 +""" +premium question +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def __init__(self): + self.exists = False + self.root = None # need to handle 0 + self.total_sum = None + + def checkEqualTree(self, root: TreeNode) -> bool: + """ + two passes + 1st pass, get total sum + 2nd pass, check whether has sum/2 + space: O(log N) + + two save 2nd pass, store sums + space: O(N) + """ + self.root = root + self.total_sum = self.dfs(root) + self.dfs(root) + return self.exists + + def dfs(self, node): + if not node: + return 0 + + l = self.dfs(node.left) + r = self.dfs(node.right) + s = l + r + node.val + if node != self.root and self.total_sum != None and self.total_sum == s * 2: + self.exists = True + + return s From b0654fa3966000f87ec69594986ff0646f5a319d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 25 Apr 2019 23:17:39 -0700 Subject: [PATCH 285/344] 505 --- 505 The Maze II.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 505 The Maze II.py diff --git a/505 The Maze II.py b/505 The Maze II.py new file mode 100644 index 0000000..f7247b7 --- /dev/null +++ b/505 The Maze II.py @@ -0,0 +1,48 @@ +#!/usr/bin/python3 +""" +premium question +""" +from typing import List +import heapq + + +dirs = [(0, -1), (0, 1), (-1, 0), (1, 0)] + + +class Solution: + def shortestDistance(self, maze: List[List[int]], start: List[int], destination: List[int]) -> int: + """ + No friction rolling ball + + F[i][j][dir] = min distance given direction + S[i][j] = whether stoppable + + Dijkstra's algorith, reduce to a graph problem + """ + m, n = len(maze), len(maze[0]) + D = [[float("inf") for _ in range(n)] for _ in range(m)] # distance matrix + i, j = start + D[i][j] = 0 + q = [(0, i, j)] + while q: + dist, i, j = heapq.heappop(q) + for di, dj in dirs: + cur_dist = 0 + I = i + J = j + # look ahead + while 0 <= I + di < m and 0 <= J + dj < n and maze[I + di][J + dj] == 0: + I += di + J += dj + cur_dist += 1 + + if dist + cur_dist < D[I][J]: + D[I][J] = dist + cur_dist + heapq.heappush(q, (D[I][J], I, J)) + + i, j = destination + return D[i][j] if D[i][j] != float("inf") else -1 + + +if __name__ == "__main__": + assert Solution().shortestDistance([[0,0,1,0,0],[0,0,0,0,0],[0,0,0,1,0],[1,1,0,1,1],[0,0,0,0,0]], [0,4], [4,4]) == 12 From eba7c5ff781625875479720d867319ed613c1fd2 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 28 Apr 2019 21:16:55 -0700 Subject: [PATCH 286/344] 472 --- 472 Concatenated Words.py | 112 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 472 Concatenated Words.py diff --git a/472 Concatenated Words.py b/472 Concatenated Words.py new file mode 100644 index 0000000..fe77022 --- /dev/null +++ b/472 Concatenated Words.py @@ -0,0 +1,112 @@ +#!/usr/bin/python3 +""" +Given a list of words (without duplicates), please write a program that returns +all concatenated words in the given list of words. +A concatenated word is defined as a string that is comprised entirely of at +least two shorter words in the given array. + +Example: +Input: ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat", +"ratcatdogcat"] + +Output: ["catsdogcats","dogcatsdog","ratcatdogcat"] + +Explanation: "catsdogcats" can be concatenated by "cats", "dog" and "cats"; + "dogcatsdog" can be concatenated by "dog", "cats" and "dog"; +"ratcatdogcat" can be concatenated by "rat", "cat", "dog" and "cat". + +Note: +The number of elements of the given array will not exceed 10,000 +The length sum of elements in the given array will not exceed 600,000. +All the input string will only include lower case letters. +The returned elements order does not matter. +""" +from typing import List +from collections import defaultdict + + +class Solution: + def __init__(self): + TrieNode = lambda: defaultdict(TrieNode) # not defaultdict(lambda: TrieNode) + self.root = TrieNode() # root of tire + + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + """ + Trie + DFS + """ + words.sort(key=len) + ret = [] + for w in words: + if self.can_concat(w, 0): + ret.append(w) + + cur = self.root + for c in w: + cur = cur[c] + cur["end"] = True + + return ret + + def can_concat(self, word, lo): + if not word: + return False + + k = len(word) + if lo >= k: + return True + + cur = self.root + for i in range(lo, k): + cur = cur[word[i]] + if cur.get("end", False) and self.can_concat(word, i + 1): + return True + + return False + + +class SolutionTLE: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + """ + Trie check cannot be greedy: cat sdog vs cats dog + + Sort + Trie dfs + What is the complexity? + + Word break DP + for a specific word + F[i] means word[:i] can be formed using shorter words + + complexity + O(n) * O(k^2) * O(k) + n words * get F * compare words + + Hard question is solving a collections of medium problems + """ + ret = [] + # words.sort() # sorting is unnecessary + visited = set(words) + for w in words: + if self.can_concat(w, visited): + ret.append(w) + + return ret + + def can_concat(self, w, visited): + if not w: + return False + + k = len(w) + F = [False for _ in range(k + 1)] + F[0] = True + for i in range(1, k + 1): + for j in range(i): + if j == 0 and i == k: + continue # word itself + if F[j] and w[j:i] in visited: + F[i] = True + + return F[k] + + +if __name__ == "__main__": + assert Solution().findAllConcatenatedWordsInADict(["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"]) == ["catsdogcats","dogcatsdog","ratcatdogcat"] From 3f9da86f24da77dfc7473d31b1e7dfb97bc162ef Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 28 Apr 2019 22:53:00 -0700 Subject: [PATCH 287/344] 964 --- 964 Least Operators to Express Number.py | 96 ++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 964 Least Operators to Express Number.py diff --git a/964 Least Operators to Express Number.py b/964 Least Operators to Express Number.py new file mode 100644 index 0000000..7213b3a --- /dev/null +++ b/964 Least Operators to Express Number.py @@ -0,0 +1,96 @@ +#!/usr/bin/python3 +""" +Given a single positive integer x, we will write an expression of the form +x (op1) x (op2) x (op3) x ... where each operator op1, op2, etc. is either +addition, subtraction, multiplication, or division (+, -, *, or /). For +example, with x = 3, we might write 3 * 3 / 3 + 3 - 3 which is a value of 3. + +When writing such an expression, we adhere to the following conventions: + +The division operator (/) returns rational numbers. +There are no parentheses placed anywhere. +We use the usual order of operations: multiplication and division happens before +addition and subtraction. +It's not allowed to use the unary negation operator (-). For example, "x - x" +is a valid expression as it only uses subtraction, but "-x + x" is not because +it uses negation. +We would like to write an expression with the least number of operators such +that the expression equals the given target. Return the least number of +operators used. + + +Example 1: +Input: x = 3, target = 19 +Output: 5 +Explanation: 3 * 3 + 3 * 3 + 3 / 3. The expression contains 5 operations. + +Example 2: +Input: x = 5, target = 501 +Output: 8 +Explanation: 5 * 5 * 5 * 5 - 5 * 5 * 5 + 5 / 5. The expression contains 8 +operations. + +Example 3: +Input: x = 100, target = 100000000 +Output: 3 +Explanation: 100 * 100 * 100 * 100. The expression contains 3 operations. + + +Note: +2 <= x <= 100 +1 <= target <= 2 * 10^8 +""" +from functools import lru_cache + + +class Solution: + def leastOpsExpressTarget(self, x: int, target: int) -> int: + """ + x/x is 1 + x * x is power 2 + + target = a_n * x^n + a_{n-1} * x^{n-1} + ... + a_1 * x^1 + a_0 * x/x + + To make target divisible, it can be target - a0 or target + (x^1 - a0) + """ + return self.dfs(target, x, 0) - 1 + + @lru_cache(maxsize=None) + def dfs(self, target, x, power): + """ + power: power, pow(x, power) + """ + if target == 0: + return 0 + + if target == 1: + return self.ops(power) + + d, r = target // x, target % x + ret = r * self.ops(power) + self.dfs(d, x, power + 1) + # either -r or +(x-r) + if r != 0: + ret2 = (x - r) * self.ops(power) + self.dfs(d + 1, x, power + 1) + ret = min(ret, ret2) + + return ret + + def ops(self, power): + """ + number of ops required + + + x/x + + x + + x * x + + x * x * x + """ + if power == 0: + return 2 + else: + return power + + +if __name__ == "__main__": + assert Solution().leastOpsExpressTarget(3, 19) == 5 + assert Solution().leastOpsExpressTarget(5, 501) == 8 + assert Solution().leastOpsExpressTarget(2, 125046) == 50 From 38e3478fb74b5101f5710cf28b80027e5498f797 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 29 Apr 2019 23:25:31 -0700 Subject: [PATCH 288/344] 635 --- 635 Design Log Storage System.py | 76 ++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 635 Design Log Storage System.py diff --git a/635 Design Log Storage System.py b/635 Design Log Storage System.py new file mode 100644 index 0000000..8d65351 --- /dev/null +++ b/635 Design Log Storage System.py @@ -0,0 +1,76 @@ +#!/usr/bin/python3 +""" +You are given several logs that each log contains a unique id and timestamp. +Timestamp is a string that has the following format: +Year:Month:Day:Hour:Minute:Second, for example, 2017:01:01:23:59:59. All domains +are zero-padded decimal numbers. + +Design a log storage system to implement the following functions: + +void Put(int id, string timestamp): Given a log's unique id and timestamp, store +the log in your storage system. + + +int[] Retrieve(String start, String end, String granularity): Return the id of +logs whose timestamps are within the range from start to end. Start and end all +have the same format as timestamp. However, granularity means the time level for +consideration. For example, start = "2017:01:01:23:59:59", end = +"2017:01:02:23:59:59", granularity = "Day", it means that we need to find the +logs within the range from Jan. 1st 2017 to Jan. 2nd 2017. + +Example 1: +put(1, "2017:01:01:23:59:59"); +put(2, "2017:01:01:22:59:59"); +put(3, "2016:01:01:00:00:00"); +retrieve("2016:01:01:01:01:01","2017:01:01:23:00:00","Year"); // return [1,2,3], +because you need to return all logs within 2016 and 2017. +retrieve("2016:01:01:01:01:01","2017:01:01:23:00:00","Hour"); // return [1,2], +because you need to return all logs start from 2016:01:01:01 to 2017:01:01:23, +where log 3 is left outside the range. + +Note: +There will be at most 300 operations of Put or Retrieve. +Year ranges from [2000,2017]. Hour ranges from [00,23]. +Output for Retrieve has no order required. +""" +import bisect + + +class LogSystem: + def __init__(self): + """ + BST - TreeMap (java) + binary search using time stamp + """ + self.lst = [] + + def put(self, id: int, timestamp: str) -> None: + bisect.insort(self.lst, (timestamp, id)) + + def retrieve(self, s: str, e: str, gra: str) -> List[int]: + """ + Use timestamp comparison + Can convert the timestamp to number. + """ + lo = "0001:01:01:00:00:00" + hi = "9999:12:31:23:59:59" + pre = { + "Year": 4, + "Month": 7, + "Day": 10, + "Hour": 13, + "Minute": 16, + "Second": 19, + }[gra] + + s = s[:pre] + lo[pre:] + e = e[:pre] + hi[pre:] + i = bisect.bisect_left(self.lst, (s, 0)) + j = bisect.bisect_right(self.lst, (e, float("inf"))) + return [id for _, id in self.lst[i:j]] + + +# Your LogSystem object will be instantiated and called as such: +# obj = LogSystem() +# obj.put(id,timestamp) +# param_2 = obj.retrieve(s,e,gra) From 46a6aa2e4f91bcf8945f8a57a8d4bf8f3933f6fc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 1 May 2019 23:17:32 -0700 Subject: [PATCH 289/344] 924. Minimize Malware Spread --- 924 Minimize Malware Spread.py | 101 +++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 924 Minimize Malware Spread.py diff --git a/924 Minimize Malware Spread.py b/924 Minimize Malware Spread.py new file mode 100644 index 0000000..0d04e56 --- /dev/null +++ b/924 Minimize Malware Spread.py @@ -0,0 +1,101 @@ +#!/usr/bin/python3 +""" +In a network of nodes, each node i is directly connected to another node j if +and only if graph[i][j] = 1. + +Some nodes initial are initially infected by malware. Whenever two nodes are +directly connected and at least one of those two nodes is infected by malware, +both nodes will be infected by malware. This spread of malware will continue +until no more nodes can be infected in this manner. + +Suppose M(initial) is the final number of nodes infected with malware in the +entire network, after the spread of malware stops. + +We will remove one node from the initial list. Return the node that if removed, +would minimize M(initial). If multiple nodes could be removed to minimize +M(initial), return such a node with the smallest index. + +Note that if a node was removed from the initial list of infected nodes, it may +still be infected later as a result of the malware spread. + +Example 1: +Input: graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1] +Output: 0 + +Example 2: +Input: graph = [[1,0,0],[0,1,0],[0,0,1]], initial = [0,2] +Output: 0 + +Example 3: +Input: graph = [[1,1,1],[1,1,1],[1,1,1]], initial = [1,2] +Output: 1 + +Note: +1 < graph.length = graph[0].length <= 300 +0 <= graph[i][j] == graph[j][i] <= 1 +graph[i][i] = 1 +1 <= initial.length < graph.length +0 <= initial[i] < graph.length +""" +from typing import List +from collections import defaultdict + + +class DisjointSet: + def __init__(self): + self.pi = {} + + def union(self, x, y): + pi_x = self.find(x) + pi_y = self.find(y) + self.pi[pi_x] = pi_y + + def find(self, x): + if x not in self.pi: + self.pi[x] = x + if self.pi[x] != x: + self.pi[x] = self.find(self.pi[x]) + return self.pi[x] + + +class Solution: + def minMalwareSpread(self, graph: List[List[int]], initial: List[int]) -> int: + """ + DisjointSet. But how to use DisjointSet? + + Ensure each component, the element points to a common ancestor. + The ancestor uniquely identify the component + + Each component has size. If there are only one malware in the component, + then the component can be sanitized. + """ + ds = DisjointSet() + n = len(graph) # nbr matrix + for i in range(n): + for j in range(n): + if graph[i][j] == 1: + ds.union(i, j) + + counts = defaultdict(int) # count of element in the component + for i in range(n): + counts[ds.find(i)] += 1 + + malware_counts = defaultdict(int) + for i in initial: + malware_counts[ds.find(i)] += 1 + + max_i = min(initial) + for i in initial: + pi = ds.find(i) + if malware_counts[pi] == 1: + max_count = counts[ds.find(max_i)] + if max_count < counts[pi]: + max_i = i + elif max_count == counts[pi] and max_i > i: + max_i = i + + return max_i + + +if __name__ == "__main__": + assert Solution().minMalwareSpread([[1,1,0],[1,1,0],[0,0,1]], [0,1]) == 0 From 9cda3425febd373d0e73e64c4abf0794c78eb3a2 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 1 May 2019 23:18:11 -0700 Subject: [PATCH 290/344] 527 Word Abbreviation --- 527 Word Abbreviation.py | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 527 Word Abbreviation.py diff --git a/527 Word Abbreviation.py b/527 Word Abbreviation.py new file mode 100644 index 0000000..3d22733 --- /dev/null +++ b/527 Word Abbreviation.py @@ -0,0 +1,56 @@ +#!/usr/bin/python3 +""" +premium question +""" + +from typing import List +from collections import defaultdict + + +class Solution: + def wordsAbbreviation(self, words: List[str]) -> List[str]: + """ + sort the word, check prefix and last word + + group by first and last char, group by prefix and last char + then make a trie - hard to implement + """ + hm = defaultdict(list) + ret = [None for _ in words] + for i, w in enumerate(words): + hm[w[0], w[-1], len(w)].append(i) + + TrieNode = lambda: defaultdict(TrieNode) + + for lst in hm.values(): + root = TrieNode() + for i in lst: + w = words[i] + cur = root + for c in w: + cur = cur[c] + cur["count"] = cur.get("count", 0) + 1 + + for i in lst: + w = words[i] + prefix_l = 0 + cur = root + for c in w: + prefix_l += 1 + cur = cur[c] + if cur["count"] == 1: + break + + ret[i] = self.abbrev(w, prefix_l) + + return ret + + def abbrev(self, w, prefix_l): + abbrev_l = len(w) - 2 - prefix_l + 1 + if abbrev_l > 1: + return w[:prefix_l] + str(abbrev_l) + w[-1] + return w + + +if __name__ == "__main__": + assert Solution().wordsAbbreviation(["like", "god", "internal", "me", "internet", "interval", "intension", "face", "intrusion"]) == ["l2e","god","internal","me","i6t","interval","inte4n","f2e","intr4n"] From 972d1a57a375d2269b4d570f2bcea60fba0be043 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 2 May 2019 23:40:20 -0700 Subject: [PATCH 291/344] 928 --- 928 Minimize Malware Spread II.py | 102 ++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 928 Minimize Malware Spread II.py diff --git a/928 Minimize Malware Spread II.py b/928 Minimize Malware Spread II.py new file mode 100644 index 0000000..1f66dd6 --- /dev/null +++ b/928 Minimize Malware Spread II.py @@ -0,0 +1,102 @@ +#!/usr/bin/python3 +""" +(This problem is the same as Minimize Malware Spread, with the differences +bolded.) + +In a network of nodes, each node i is directly connected to another node j if +and only if graph[i][j] = 1. + +Some nodes initial are initially infected by malware. Whenever two nodes are +directly connected and at least one of those two nodes is infected by malware, +both nodes will be infected by malware. This spread of malware will continue +until no more nodes can be infected in this manner. + +Suppose M(initial) is the final number of nodes infected with malware in the +entire network, after the spread of malware stops. + +We will remove one node from the initial list, completely removing it and any +connections from this node to any other node. Return the node that if removed, would minimize M(initial). If multiple nodes could be removed to minimize M(initial), return such a node with the smallest index. + +Example 1: +Input: graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1] +Output: 0 + +Example 2: +Input: graph = [[1,1,0],[1,1,1],[0,1,1]], initial = [0,1] +Output: 1 + +Example 3: +Input: graph = [[1,1,0,0],[1,1,1,0],[0,1,1,1],[0,0,1,1]], initial = [0,1] +Output: 1 + + +Note: +1 < graph.length = graph[0].length <= 300 +0 <= graph[i][j] == graph[j][i] <= 1 +graph[i][i] = 1 +1 <= initial.length < graph.length +0 <= initial[i] < graph.length +""" +from typing import List +from collections import defaultdict + + +class DisjointSet: + def __init__(self): + self.pi = {} + + def union(self, x, y): + self.pi[self.find(x)] = self.find(y) + + def find(self, x): + if x not in self.pi: + self.pi[x] = x + if self.pi[x] != x: + self.pi[x] = self.find(self.pi[x]) + return self.pi[x] + + +class Solution: + def minMalwareSpread(self, graph: List[List[int]], initial: List[int]) -> int: + """ + DisjointSet? DisjointSet cannot remove connections + + Then don't add the connections from the malware at all + + For each component of G, either it neighbors 0, 1, or >= 2 nodes from + initial. The result only changes if there is exactly 1 neighbor from + initial, so we need a way to count this. + """ + n = len(graph) + initial_set = set(initial) + normal = [i for i in range(n) if i not in initial_set] + ds = DisjointSet() + for i in normal: + for j in normal: + if graph[i][j] == 1: + ds.union(i, j) + + sizes = defaultdict(int) + for i in normal: + sizes[ds.find(i)] += 1 + + comp2malcount = defaultdict(int) + mal2comps = defaultdict(set) + for i in normal: + for j in initial: + if graph[i][j] == 1: + comp2malcount[ds.find(i)] += 1 + mal2comps[j].add(ds.find(i)) + + idx = min(initial) + max_size = 0 + for j in initial: + for comp in mal2comps[j]: + if comp2malcount[comp] == 1: + if sizes[comp] > max_size: + max_size = sizes[comp] + idx = j + elif sizes[comp] == max_size: + idx = min(idx, j) + + return idx From 420d1df0f951e845463bb6cd144c72e8909bcb30 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 5 May 2019 23:52:28 -0700 Subject: [PATCH 292/344] update --- 035 Valid Sudoku.py | 12 ++++++++---- 093 Restore IP Addresses.py | 17 +++++++++-------- 332 Reconstruct Itinerary.py | 13 +++++++++---- 351 Android Unlock Patterns.py | 11 ++++++++--- 403 Frog Jump.py | 8 +++++--- 527 Word Abbreviation.py | 8 +++++--- 928 Minimize Malware Spread II.py | 4 +++- 986 Interval List Intersections.py | 10 ++++++++++ 8 files changed, 57 insertions(+), 26 deletions(-) diff --git a/035 Valid Sudoku.py b/035 Valid Sudoku.py index cc22a81..caf842b 100644 --- a/035 Valid Sudoku.py +++ b/035 Valid Sudoku.py @@ -12,8 +12,12 @@ class Solution: def isValidSudoku(self, board): """ + Brute force - check rows, cols, and squares and maintain a hashmap to store the previously seen elements + Notice how check the square in the board. + Save space by one iterations. + 9 squares are iterated by i 9 cells are iterated by j Squares lie on 3 big rows; index for the 3 big rows: i/3*3-th, thus iteration pattern: 000, 333, 666 @@ -22,14 +26,14 @@ def isValidSudoku(self, board): Squares lie on 3 big column; index for the 3 big column: i%3*3-th, thus iteration pattern: (036, 036, 036) Subdivide the 1 big column into 3 small column; index for the 3 small columns: j%3-th, thus iteration pattern 012, 012, 012) - thus, iterate by board[i/3*3+j/3][i%3*3+j%3] + thus, iterate by board[i/3*3 + j/3][i%3*3 + j%3] :param board: a 9x9 2D array :return: boolean """ # check row & column for i in xrange(9): - row = [] + row = [] # change to hashamp column = [] square = [] for j in xrange(9): @@ -55,7 +59,7 @@ def isValidSudoku(self, board): # check square try: - square_element = int(board[i/3*3+j/3][i%3*3+j%3]) + square_element = int(board[i/3*3 + j/3][i%3*3 + j%3]) if square_element in square: return False else: @@ -69,4 +73,4 @@ def isValidSudoku(self, board): assert Solution().isValidSudoku( ["..4...63.", ".........", "5......9.", "...56....", "4.3.....1", "...7.....", "...5.....", ".........", "........."] - )==False \ No newline at end of file + )==False diff --git a/093 Restore IP Addresses.py b/093 Restore IP Addresses.py index 992c8f1..9c44ba6 100644 --- a/093 Restore IP Addresses.py +++ b/093 Restore IP Addresses.py @@ -7,6 +7,8 @@ return ["255.255.11.135", "255.255.111.35"]. (Order does not matter) """ __author__ = 'Danyang' + + class Solution: def restoreIpAddresses(self, s): """ @@ -27,11 +29,11 @@ def dfs_complicated(self, seq, cur, result): :param result: :return: """ - if len(cur)>4: + if len(cur) > 4: return if not cur or self.is_valid(cur[-1]): - if len(cur)==4 and not seq: # check the last one first + if len(cur) == 4 and not seq: # check the last one first result.append(".".join(cur)) return @@ -57,20 +59,19 @@ def dfs(self, seq, cur, result): # for i in xrange(1, 3+1): # for loop - for i in xrange(1, min(3, len(seq))+1): + for i in xrange(1, min(3, len(seq)) + 1): new_seg = seq[:i] # condition check - if len(cur)<4 and self.is_valid(new_seg): - self.dfs(seq[i:], cur+[new_seg], result) + if len(cur) < 4 and self.is_valid(new_seg): + self.dfs(seq[i:], cur + [new_seg], result) else: return - def is_valid(self, s): if not s: return False - return s=="0" or s[0]!="0" and 0<=int(s)<256 # ["0.0.0.0"] + return s == "0" or s[0]!="0" and 0<= int(s) <256 # ["0.0.0.0"] if __name__=="__main__": IP = "25525511135" - print Solution().restoreIpAddresses(IP) \ No newline at end of file + print Solution().restoreIpAddresses(IP) diff --git a/332 Reconstruct Itinerary.py b/332 Reconstruct Itinerary.py index 5e26c41..957ad27 100644 --- a/332 Reconstruct Itinerary.py +++ b/332 Reconstruct Itinerary.py @@ -24,15 +24,20 @@ class Solution(object): def findItinerary(self, tickets): """ - Euler path + Euler path: + An Euler path is a path that uses every edge of a graph exactly once. + Hierholzer's algorithm a Euler path, must be directed graph The graph must be directed graph + + Heap can be replaced by stack/queue and sort the original list + + The ret is build as from right to left: JFK <- NRT <- JFK <- KUL :type tickets: List[List[str]] :rtype: List[str] """ - G = defaultdict(list) - for elt in tickets: - s, e = elt + G = defaultdict(list) # every list is a heap + for s, e in tickets: heapq.heappush(G[s], e) # heap lexical order ret = deque() diff --git a/351 Android Unlock Patterns.py b/351 Android Unlock Patterns.py index bcb893a..aabc256 100644 --- a/351 Android Unlock Patterns.py +++ b/351 Android Unlock Patterns.py @@ -7,7 +7,8 @@ class Solution(object): def __init__(self): """ - encode rules + Skip matrix + Encode rule for 2, 4, 6, 8, 5 """ self.skip = [[None for _ in xrange(10)] for _ in xrange(10)] self.skip[1][3], self.skip[3][1] = 2, 2 @@ -21,7 +22,7 @@ def __init__(self): def numberOfPatterns(self, m, n): """ - NP + NP - O(N!) dfs Maintain a skip matrix @@ -38,6 +39,10 @@ def numberOfPatterns(self, m, n): ) def dfs(self, cur, visited, remain): + """ + Return the count of combination + Optimization - memoization + """ if remain == 1: return 1 @@ -58,4 +63,4 @@ def dfs(self, cur, visited, remain): if __name__ == "__main__": assert Solution().numberOfPatterns(1, 2) == 65 - assert Solution().numberOfPatterns(1, 3) == 385 \ No newline at end of file + assert Solution().numberOfPatterns(1, 3) == 385 diff --git a/403 Frog Jump.py b/403 Frog Jump.py index 80c1427..476c3df 100644 --- a/403 Frog Jump.py +++ b/403 Frog Jump.py @@ -39,8 +39,10 @@ class Solution(object): def canCross(self, stones): """ + Instead of having F[i][step] = True/False, use F[i] = set of steps + F, step table - Let F[i] be stone at position i, + Let F[i] be all possible steps at stone i. dp with a set as the table cell. :type stones: List[int] @@ -54,9 +56,9 @@ def canCross(self, stones): for stone in stones: for step in F[stone]: for i in (-1, 0, 1): - nxt = stone+step+i + nxt = stone + step + i if nxt != stone and nxt in F: - F[nxt].add(step+i) + F[nxt].add(step + i) return True if F[stones[-1]] else False diff --git a/527 Word Abbreviation.py b/527 Word Abbreviation.py index 3d22733..a06d876 100644 --- a/527 Word Abbreviation.py +++ b/527 Word Abbreviation.py @@ -10,10 +10,12 @@ class Solution: def wordsAbbreviation(self, words: List[str]) -> List[str]: """ - sort the word, check prefix and last word + Sort the word, check prefix and last word - group by first and last char, group by prefix and last char - then make a trie - hard to implement + Group by first and last char, group by prefix and last char + then make a trie - hard to implement? TrieNode lambda + + Need to count the #appearances in the TrieNode """ hm = defaultdict(list) ret = [None for _ in words] diff --git a/928 Minimize Malware Spread II.py b/928 Minimize Malware Spread II.py index 1f66dd6..74cfef4 100644 --- a/928 Minimize Malware Spread II.py +++ b/928 Minimize Malware Spread II.py @@ -15,7 +15,9 @@ entire network, after the spread of malware stops. We will remove one node from the initial list, completely removing it and any -connections from this node to any other node. Return the node that if removed, would minimize M(initial). If multiple nodes could be removed to minimize M(initial), return such a node with the smallest index. +connections from this node to any other node. Return the node that if removed, +would minimize M(initial). If multiple nodes could be removed to minimize +M(initial), return such a node with the smallest index. Example 1: Input: graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1] diff --git a/986 Interval List Intersections.py b/986 Interval List Intersections.py index ec97cfc..e1983b8 100644 --- a/986 Interval List Intersections.py +++ b/986 Interval List Intersections.py @@ -34,6 +34,16 @@ def __init__(self, s=0, e=0): class Solution: def intervalIntersection(self, A: List[Interval], B: List[Interval]) -> List[Interval]: """ + Among the given intervals, consider the interval A[0] with the smallest + endpoint. (Without loss of generality, this interval occurs in array A.) + Then, among the intervals in array B, A[0] can only intersect one such + interval in array B. (If two intervals in B intersect A[0], then they + both share the endpoint of A[0] -- but intervals in B are disjoint, which + is a contradiction.) + + If A[0] has the smallest endpoint, it can only intersect B[0]. After, we + can discard A[0] since it cannot intersect anything else. + merge by checking max starts and min ends pop by ends """ From b5c08c0058de4898a028d03e3fa9491622c933ab Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 6 May 2019 00:31:25 -0700 Subject: [PATCH 293/344] 140 --- 140 Word Break II.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/140 Word Break II.py b/140 Word Break II.py index 1532550..e57f588 100644 --- a/140 Word Break II.py +++ b/140 Word Break II.py @@ -11,6 +11,9 @@ A solution is ["cats and dog", "cat sand dog"]. """ __author__ = 'Danyang' +from collections import deque + + class Solution: def wordBreak(self, s, dict): """ @@ -37,39 +40,44 @@ def wordBreak(self, s, dict): :return: a list of strings """ # dp = [[]] * (len(s) + 1) # namespace reuse - dp = [[] for _ in range(len(s)+1)] + dp = [[] for _ in range(len(s) + 1)] dp[0].append("dummy") for i in range(len(s)): if not dp[i]: continue + for word in dict: - if s[i: i+len(word)]==word: - dp[i+len(word)].append(word) + if s[i:i + len(word)] == word: + dp[i + len(word)].append(word) # build result if not dp[-1]: return [] result = [] - cur_sentence = [] - self.__build_result(dp, len(s), cur_sentence, result) + self.build_result(dp, len(s), deque(), result) return result - def __build_result(self, dp, cur_index, cur_sentence, result): + def build_result(self, dp, cur_index, cur_sentence, result): """ dfs recursive + + from right to left """ # reached, build the result from cur_sentence - if cur_index==0: - result.append(" ".join(cur_sentence[::-1])) + if cur_index == 0: + result.append(" ".join(cur_sentence)) return # dfs for prefix in dp[cur_index]: - self.__build_result(dp, cur_index-len(prefix), cur_sentence+[prefix], result) + cur_sentence.appendleft(prefix) + self.build_result(dp, cur_index - len(prefix), cur_sentence, result) + cur_sentence.popleft() + if __name__=="__main__": - assert Solution().wordBreak("catsanddog", ["cat", "cats", "and", "sand", "dog"])==['cat sand dog', 'cats and dog'] \ No newline at end of file + assert Solution().wordBreak("catsanddog", ["cat", "cats", "and", "sand", "dog"])==['cat sand dog', 'cats and dog'] From 85bf4ea1f78b9910dadac56a6ba4572bc6c8de21 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 6 May 2019 17:50:45 -0700 Subject: [PATCH 294/344] 068 Text Justification py3 --- 068 Text Justification py3.py | 93 +++++++++++++++++++++++++++++++++++ 070 Text Justification.py | 4 +- 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 068 Text Justification py3.py diff --git a/068 Text Justification py3.py b/068 Text Justification py3.py new file mode 100644 index 0000000..0eac360 --- /dev/null +++ b/068 Text Justification py3.py @@ -0,0 +1,93 @@ +#!/usr/bin/python3 +""" +Given an array of words and a width maxWidth, format the text such that each line has exactly maxWidth characters and is fully (left and right) justified. + +You should pack your words in a greedy approach; that is, pack as many words as you can in each line. Pad extra spaces ' ' when necessary so that each line has exactly maxWidth characters. + +Extra spaces between words should be distributed as evenly as possible. If the number of spaces on a line do not divide evenly between words, the empty slots on the left will be assigned more spaces than the slots on the right. + +For the last line of text, it should be left justified and no extra space is inserted between words. + +Note: + +A word is defined as a character sequence consisting of non-space characters only. +Each word's length is guaranteed to be greater than 0 and not exceed maxWidth. +The input array words contains at least one word. +Example 1: + +Input: +words = ["This", "is", "an", "example", "of", "text", "justification."] +maxWidth = 16 +Output: +[ + "This is an", + "example of text", + "justification. " +] +Example 2: + +Input: +words = ["What","must","be","acknowledgment","shall","be"] +maxWidth = 16 +Output: +[ + "What must be", + "acknowledgment ", + "shall be " +] +Explanation: Note that the last line is "shall be " instead of "shall be", + because the last line must be left-justified instead of fully-justified. + Note that the second line is also left-justified becase it contains only one word. +Example 3: + +Input: +words = ["Science","is","what","we","understand","well","enough","to","explain", + "to","a","computer.","Art","is","everything","else","we","do"] +maxWidth = 20 +Output: +[ + "Science is what we", + "understand well", + "enough to explain to", + "a computer. Art is", + "everything else we", + "do " +] +""" +from typing import List + + +class Solution: + def fullJustify(self, words: List[str], maxWidth: int) -> List[str]: + """ + Round robin distribution of spaces + """ + ret = [] + char_cnt = 0 + cur_words = [] + + for w in words: + # len(cur_words) is the space needed with len(cur_words) + 1 words + if char_cnt + len(w) + len(cur_words) > maxWidth: + # break, move w into the next line + # Round robin distribut the spaces except for the last word + for i in range(maxWidth - char_cnt): + cur_words[i % max(1, len(cur_words) - 1)] += " " + # len(cur_words) - 1 can be 0 + ret.append("".join(cur_words)) + + cur_words = [] + char_cnt = 0 + + cur_words.append(w) + char_cnt += len(w) + + # last line + last = " ".join(cur_words) + ret.append(last + " " * (maxWidth - len(last))) + return ret + + +if __name__ == "__main__": + assert Solution().fullJustify(["This", "is", "an", "example", "of", "text", "justification."], 16) == ["This is an","example of text","justification. "] + assert Solution().fullJustify(["What","must","be","acknowledgment","shall","be"], 16) == ["What must be","acknowledgment ","shall be "] diff --git a/070 Text Justification.py b/070 Text Justification.py index 6e46ff2..ef48fc8 100644 --- a/070 Text Justification.py +++ b/070 Text Justification.py @@ -29,6 +29,8 @@ In this case, that line should be left-justified. """ __author__ = 'Danyang' + + class Solution: def fullJustify(self, words, L): """ @@ -95,4 +97,4 @@ def distribute_space(self, L, result): if __name__=="__main__": print Solution().fullJustify(["This", "is", "an", "example", "of", "text", "justification."], 16) - print Solution().fullJustify(["What","must","be","shall","be."], 12) \ No newline at end of file + print Solution().fullJustify(["What","must","be","shall","be."], 12) From 732ffc2a2d95462137e885b991f548c22fe55bf3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 6 May 2019 18:04:15 -0700 Subject: [PATCH 295/344] 663. Equal Tree Partition --- 663 Equal Tree Partition.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/663 Equal Tree Partition.py b/663 Equal Tree Partition.py index 1ba8520..b93549d 100644 --- a/663 Equal Tree Partition.py +++ b/663 Equal Tree Partition.py @@ -13,6 +13,33 @@ def __init__(self, x): class Solution: def __init__(self): + self.sums = [] + + def checkEqualTree(self, root: TreeNode) -> bool: + """ + To save 2nd pass, store sums + space: O(N) + """ + self.dfs(root) + total = self.sums.pop() + return total % 2 == 0 and total // 2 in self.sums + + def dfs(self, node): + if not node: + return 0 + + l = self.dfs(node.left) + r = self.dfs(node.right) + s = l + r + node.val + self.sums.append(s) + return s + + +class Solution: + def __init__(self): + """ + Save space, two passes + """ self.exists = False self.root = None # need to handle 0 self.total_sum = None @@ -24,7 +51,7 @@ def checkEqualTree(self, root: TreeNode) -> bool: 2nd pass, check whether has sum/2 space: O(log N) - two save 2nd pass, store sums + To save 2nd pass, store sums space: O(N) """ self.root = root From 6903ae580e32d2e649f4846d724aa22b5f020643 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 6 May 2019 19:07:23 -0700 Subject: [PATCH 296/344] 392 Is Subsequence --- 392 Is Subsequence py3.py | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 392 Is Subsequence py3.py diff --git a/392 Is Subsequence py3.py b/392 Is Subsequence py3.py new file mode 100644 index 0000000..45de13d --- /dev/null +++ b/392 Is Subsequence py3.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 +""" +Given a string s and a string t, check if s is subsequence of t. + +You may assume that there is only lower case English letters in both s and t. t +is potentially a very long (length ~= 500,000) string, and s is a short string +(<=100). + +A subsequence of a string is a new string which is formed from the original +string by deleting some (can be none) of the characters without disturbing the +relative positions of the remaining characters. (ie, "ace" is a subsequence of +"abcde" while "aec" is not). + +Example 1: +s = "abc", t = "ahbgdc" +Return true. + +Example 2: +s = "axc", t = "ahbgdc" +Return false. + +Follow up: +If there are lots of incoming S, say S1, S2, ... , Sk where k >= 1B, and you +want to check one by one to see if T has its subsequence. In this scenario, how +would you change your code? + +Credits: +Special thanks to @pbrother for adding this problem and creating all test cases +""" +from bisect import bisect_left +from collections import defaultdict + + +class Solution: + def isSubsequence(self, s: str, t: str) -> bool: + """ + Subsequence - Binary search + """ + char_pos = defaultdict(list) + for p, c in enumerate(t): + char_pos[c].append(p) + # the list is naturally sorted + + lo_po = -1 + for c in s: + if c not in char_pos: + return False + pos = char_pos[c] + i = bisect_left(pos, lo_po) + if i == len(pos): + return False + lo_po = pos[i] + 1 # pitfall + + return True + + +if __name__ == "__main__": + assert Solution().isSubsequence("abc", "ahbgdc") == True + assert Solution().isSubsequence("acb", "ahbgdc") == False From 904cf6bef1f0d75d73b70bace82be238db26b059 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 6 May 2019 20:27:13 -0700 Subject: [PATCH 297/344] 295. Find Median from Data Stream --- 295 Find Median from Data Stream.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/295 Find Median from Data Stream.py b/295 Find Median from Data Stream.py index 0412106..137014d 100644 --- a/295 Find Median from Data Stream.py +++ b/295 Find Median from Data Stream.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value. @@ -28,7 +29,10 @@ class DualHeap(object): def __init__(self): """ Dual Heap is great in the case where there is no removal. - :return: + + ----------> number line + Δ Δ + max min """ self.min_h = [] self.max_h = [] @@ -43,25 +47,26 @@ def insert(self, num): def balance(self): l1 = len(self.min_h) l2 = len(self.max_h) - if l1-l2 > 1: + if abs(l1 - l2) <= 1: + return + elif l1 - l2 > 1: heapq.heappush(self.max_h, -heapq.heappop(self.min_h)) self.balance() - elif l2-l1 > 1: + else: heapq.heappush(self.min_h, -heapq.heappop(self.max_h)) self.balance() - return def get_median(self): l1 = len(self.min_h) l2 = len(self.max_h) - m = (l1+l2-1)/2 - if (l1+l2) % 2 == 1: - if m == l2-1: + if (l1 + l2) % 2 == 1: + m = (l1 + l2) / 2 # median index, equivalent to (l1 + l2 - 1) / 2 + if m < l2: return -self.max_h[0] else: return self.min_h[0] else: - return (-self.max_h[0]+self.min_h[0])/2.0 + return (-self.max_h[0] + self.min_h[0]) / 2.0 class MedianFinder(object): From 13860e3f092b68719f6413ac4ffd3093b824a281 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 6 May 2019 21:08:16 -0700 Subject: [PATCH 298/344] 227 Basic Calculator II --- 227 Basic Calculator II py3.py | 89 ++++++++++++++++++++++++++++++++++ 227 Basic Calculator II.py | 12 +++-- 2 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 227 Basic Calculator II py3.py diff --git a/227 Basic Calculator II py3.py b/227 Basic Calculator II py3.py new file mode 100644 index 0000000..983437f --- /dev/null +++ b/227 Basic Calculator II py3.py @@ -0,0 +1,89 @@ +#!/usr/bin/python3 +""" +Implement a basic calculator to evaluate a simple expression string. + +The expression string contains only non-negative integers, +, -, *, / operators +and empty spaces . The integer division should truncate toward zero. + +Example 1: + +Input: "3+2*2" +Output: 7 +Example 2: + +Input: " 3/2 " +Output: 1 +Example 3: + +Input: " 3+5 / 2 " +Output: 5 +Note: + +You may assume that the given expression is always valid. +Do not use the eval built-in library function. +""" + + +class Solution: + def calculate(self, s: str) -> int: + """ + No brackets. Look at previous operand and operator, when finishing + scanning current operand. + """ + operand = 0 + stk = [] + prev_op = "+" + for i, c in enumerate(s): + if c.isdigit(): + operand = operand * 10 + int(c) + + # i == len(s) - 1 + delimited = c in ("+", "-", "*", "/") or i == len(s) - 1 + if delimited: + if prev_op == "+": + cur = operand + elif prev_op == "-": + cur = -operand + elif prev_op == "*": + cur = stk.pop() * operand + else: + assert prev_op == "/" + # instead of op1 // op2 due to negative handling, -3 // 2 == -2 + cur = int(stk.pop() / operand) + + stk.append(cur) + prev_op = c + operand = 0 + + return sum(stk) + + def calculate_error(self, s: str) -> int: + """ + cannot use dictionary, since it is eager evaluation + """ + operand = 0 + stk = [] + prev_op = "+" + for i, c in enumerate(s): + if c.isdigit(): + operand = operand * 10 + int(c) + + # i == len(s) - 1 + delimited = c in ("+", "-", "*", "/") or i == len(s) - 1 + if delimited: + cur = { + "+": operand, + "-": -operand, + "*": stk.pop() * operand, + "/": int(stk.pop() / operand), # instead of op1 // op2 due to negative handling, -3 // 2 == -2 + }[prev_op] + stk.append(cur) + + prev_op = c + operand = 0 + + return sum(stk) + + +if __name__ == "__main__": + assert Solution().calculate("3+2*2") == 7 diff --git a/227 Basic Calculator II.py b/227 Basic Calculator II.py index d8185ea..699a297 100644 --- a/227 Basic Calculator II.py +++ b/227 Basic Calculator II.py @@ -23,11 +23,14 @@ def calculate(self, s): :type s: str :rtype: int """ - lst = self.to_list(s) + lst = self.parse(s) post = self.infix2postfix(lst) return self.eval_postfix(post) - def to_list(self, s): + def parse(self, s): + """ + return tokens + """ i = 0 ret = [] while i < len(s): @@ -47,7 +50,8 @@ def to_list(self, s): return ret def infix2postfix(self, lst): - stk = [] # store operators in strictly increasing precedence + # operator stacks rather than operand + stk = [] # stk only stores operators in strictly increasing precedence ret = [] for elt in lst: if elt.isdigit(): @@ -100,4 +104,4 @@ def eval_postfix(self, post): if __name__ == "__main__": - assert Solution().calculate("3+2*2") == 7 \ No newline at end of file + assert Solution().calculate("3+2*2") == 7 From 707e8031fc71768c6213572fd58a6d507aa9edbb Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 6 May 2019 21:27:46 -0700 Subject: [PATCH 299/344] 317 Shortest Distance from All Buildings --- 317 Shortest Distance from All Buildings.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/317 Shortest Distance from All Buildings.py b/317 Shortest Distance from All Buildings.py index 8a22d72..ee50824 100644 --- a/317 Shortest Distance from All Buildings.py +++ b/317 Shortest Distance from All Buildings.py @@ -12,6 +12,17 @@ def __init__(self): def shortestDistance(self, grid): """ + BFS & collect all distance + + ideas: + Pruning: don't use a fresh "visited" for each BFS. Instead, I walk only + onto the cells that were reachable from all previous buildings. From the + first building I only walk onto cells where grid is 0, and make them -1. + From the second building I only walk onto cells where grid is -1, and I + make them -2. + -1 + -2 + -3 :type grid: List[List[int]] :rtype: int """ @@ -72,4 +83,4 @@ def bfs(self, grid, acc, reachable, x, y): [[1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 1], [0, 1, 1, 0, 0, 1], [1, 0, 0, 1, 0, 1], [1, 0, 1, 0, 0, 1], [1, 0, 0, 0, 0, 1], [0, 1, 1, 1, 1, 0]]) == 88 assert Solution().shortestDistance([[1, 2, 0]]) == -1 - assert Solution().shortestDistance([[1, 0, 2, 0, 1], [0, 0, 0, 0, 0], [0, 0, 1, 0, 0]]) == 7 \ No newline at end of file + assert Solution().shortestDistance([[1, 0, 2, 0, 1], [0, 0, 0, 0, 0], [0, 0, 1, 0, 0]]) == 7 From 4d24720ec1b6196822e3ae43546a083c2bdcec97 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 6 May 2019 21:46:43 -0700 Subject: [PATCH 300/344] 079 Word Search --- 079 Word Search py3.py | 63 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 079 Word Search py3.py diff --git a/079 Word Search py3.py b/079 Word Search py3.py new file mode 100644 index 0000000..1d8e6c0 --- /dev/null +++ b/079 Word Search py3.py @@ -0,0 +1,63 @@ +#!/usr/bin/python3 +""" +Given a 2D board and a word, find if the word exists in the grid. + +The word can be constructed from letters of sequentially adjacent cell, where +"adjacent" cells are those horizontally or vertically neighboring. The same +letter cell may not be used more than once. + +Example: + +board = +[ + ['A','B','C','E'], + ['S','F','C','S'], + ['A','D','E','E'] +] + +Given word = "ABCCED", return true. +Given word = "SEE", return true. +Given word = "ABCB", return false. +""" +from typing import List +from collections import defaultdict + + +dirs = [(0, -1), (0, 1), (1, 0), (-1, 0)] + + +class Solution: + def exist(self, board: List[List[str]], word: str) -> bool: + m, n = len(board), len(board[0]) + visited = defaultdict(lambda: defaultdict(bool)) + for i in range(m): + for j in range(n): + if board[i][j] == word[0]: + if self.dfs(board, visited, i, j, word, 1): + return True + + return False + + def dfs(self, board, visited, i, j, word, idx): + visited[i][j] = True + if idx >= len(word): + return True + + m, n = len(board), len(board[0]) + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < m and 0 <= J < n and not visited[I][J] and board[I][J] == word[idx]: + if self.dfs(board, visited, I, J, word, idx + 1): + return True + + visited[i][j] = False # restore + return False + + +if __name__ == "__main__": + assert Solution().exist([ + ["A","B","C","E"], + ["S","F","E","S"], + ["A","D","E","E"] + ], "ABCESEEEFS") == True From 230c5ffd6d5ce852b18eaf07ce427a68eea6570a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 8 May 2019 23:41:18 -0700 Subject: [PATCH 301/344] 772 Basic Calculator III --- 269 Alien Dictionary.py | 6 +-- 364 Nested List Weight Sum II.py | 6 +-- 772 Basic Calculator III.py | 75 ++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 772 Basic Calculator III.py diff --git a/269 Alien Dictionary.py b/269 Alien Dictionary.py index 8dc051c..b95e041 100644 --- a/269 Alien Dictionary.py +++ b/269 Alien Dictionary.py @@ -25,7 +25,7 @@ def alienOrder(self, words): def construct_graph(self, words): V = defaultdict(list) - for i in xrange(len(words)-1): + for i in xrange(len(words) - 1): # compare word_i and word_{i+1} for j in xrange(min(len(words[i]), len(words[i+1]))): if words[i][j] != words[i+1][j]: V[words[i][j]].append(words[i+1][j]) @@ -41,7 +41,7 @@ def topo_dfs(self, V, v, visited, pathset, ret): :param visited: visited letters :param pathset: marked predecessor in the path :param ret: the path, ordered topologically - :return: whether contains cycles + :return: whether contains cycles """ if v in pathset: return False @@ -88,4 +88,4 @@ def construct_graph_tedious(self, words, up, down, ptr, V): if __name__ == "__main__": lst = ["ze", "yf", "xd", "wd", "vd", "ua", "tt", "sz", "rd", "qd", "pz", "op", "nw", "mt", "ln", "ko", "jm", "il", "ho", "gk", "fa", "ed", "dg", "ct", "bb", "ba"] - assert Solution().alienOrder(lst) == "zyxwvutsrqponmlkjihgfedcba" \ No newline at end of file + assert Solution().alienOrder(lst) == "zyxwvutsrqponmlkjihgfedcba" diff --git a/364 Nested List Weight Sum II.py b/364 Nested List Weight Sum II.py index 76ae1fb..15236ad 100644 --- a/364 Nested List Weight Sum II.py +++ b/364 Nested List Weight Sum II.py @@ -56,7 +56,7 @@ def __init__(self): def depthSumInverse(self, nestedList): """ - NestedInteger is a union type + NestedInteger is a union type :type nestedList: List[NestedInteger] :rtype: int """ @@ -112,7 +112,3 @@ def dfs(self, nl): self.sum += sum(map(lambda x: x.getInteger() * height, ni_list)) return height - - - - diff --git a/772 Basic Calculator III.py b/772 Basic Calculator III.py new file mode 100644 index 0000000..f5f4754 --- /dev/null +++ b/772 Basic Calculator III.py @@ -0,0 +1,75 @@ +#!/usr/bin/python3 +""" +Implement a basic calculator to evaluate a simple expression string. + +The expression string may contain open ( and closing parentheses ), the plus + +or minus sign -, non-negative integers and empty spaces . + +The expression string contains only non-negative integers, +, -, *, / operators +, open ( and closing parentheses ) and empty spaces . The integer division +should truncate toward zero. + +You may assume that the given expression is always valid. All intermediate +results will be in the range of [-2147483648, 2147483647]. + +Some examples: +"1 + 1" = 2 +" 6-4 / 2 " = 4 +"2*(5+5*2)/3+(6/2+8)" = 21 +"(2+6* 3+5- (3*14/7+2)*5)+3"=-12 + +Note: Do not use the eval built-in library function. +""" + + +class Solution: + def calculate(self, s: str) -> int: + """ + recursively handle bracket + """ + s = s + "\0" # signal the end + ret, _ = self.eval(s, 0, []) + print(ret) + return ret + + def eval(self, s, i, stk): + operand = 0 + prev_op = "+" + while i < len(s): + c = s[i] + if c == " ": + i += 1 + elif c.isdigit(): + operand = operand * 10 + int(c) + i += 1 + elif c in ("+", "-", "*", "/", ")", "\0"): # delimited + if prev_op == "+": + stk.append(operand) + elif prev_op == "-": + stk.append(-operand) + elif prev_op == "*": + prev_operand = stk.pop() + stk.append(prev_operand * operand) + elif prev_op == "/": + prev_operand = stk.pop() + stk.append(int(prev_operand / operand)) + + operand = 0 + prev_op = c + i += 1 + + if c == ")": + return sum(stk), i + + elif c == "(": + operand, i = self.eval(s, i + 1, []) + # elif c == ")": + # return sum(stk), i + 1 + else: + raise + + return sum(stk), i + + +if __name__ == "__main__": + assert Solution().calculate("(2+6* 3+5- (3*14/7+2)*5)+3") == -12 From 84bb4613743c2a11252810ad94bbc94752c090ab Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 15 May 2019 11:09:26 -0700 Subject: [PATCH 302/344] 751 IP to CIDR --- 751 IP to CIDR.py | 122 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 751 IP to CIDR.py diff --git a/751 IP to CIDR.py b/751 IP to CIDR.py new file mode 100644 index 0000000..cc69b9c --- /dev/null +++ b/751 IP to CIDR.py @@ -0,0 +1,122 @@ +#!/usr/bin/python3 +""" +Given a start IP address ip and a number of ips we need to cover n, return a +representation of the range as a list (of smallest possible length) of CIDR +blocks. + +A CIDR block is a string consisting of an IP, followed by a slash, and then the +prefix length. For example: "123.45.67.89/20". That prefix length "20" +represents the number of common prefix bits in the specified range. + +Example 1: +Input: ip = "255.0.0.7", n = 10 +Output: ["255.0.0.7/32","255.0.0.8/29","255.0.0.16/32"] +Explanation: +The initial ip address, when converted to binary, looks like this (spaces added +for clarity): +255.0.0.7 -> 11111111 00000000 00000000 00000111 +The address "255.0.0.7/32" specifies all addresses with a common prefix of 32 +bits to the given address, +ie. just this one address. + +The address "255.0.0.8/29" specifies all addresses with a common prefix of 29 +bits to the given address: +255.0.0.8 -> 11111111 00000000 00000000 00001000 +Addresses with common prefix of 29 bits are: +11111111 00000000 00000000 00001000 +11111111 00000000 00000000 00001001 +11111111 00000000 00000000 00001010 +11111111 00000000 00000000 00001011 +11111111 00000000 00000000 00001100 +11111111 00000000 00000000 00001101 +11111111 00000000 00000000 00001110 +11111111 00000000 00000000 00001111 + +The address "255.0.0.16/32" specifies all addresses with a common prefix of 32 +bits to the given address, +ie. just 11111111 00000000 00000000 00010000. + +In total, the answer specifies the range of 10 ips starting with the address +255.0.0.7 . + +There were other representations, such as: +["255.0.0.7/32","255.0.0.8/30", "255.0.0.12/30", "255.0.0.16/32"], +but our answer was the shortest possible. + +Also note that a representation beginning with say, "255.0.0.7/30" would be +incorrect, +because it includes addresses like 255.0.0.4 = 11111111 00000000 00000000 +00000100 +that are outside the specified range. +Note: +ip will be a valid IPv4 address. +Every implied address ip + x (for x < n) will be a valid IPv4 address. +n will be an integer in the range [1, 1000]. +""" +from typing import List + + +# the weights of ip when converting to binary +weights = [ + 24, + 16, + 8, + 0, +] + + +class Solution: + def ipToCIDR(self, ip: str, n: int) -> List[str]: + """ + bit manipulation + 111, then 32 to cover only one, depends on LSB + Greedy + To cover n, can have representation covers > n + + need helper functions, write the main function first + + Iterate LSB to the next LSB skipping 1's + num += lsb + """ + num_ip = self.to_bin(ip) + ret = [] + while n > 0: + lsb = self.get_lsb(num_ip) + while (1 << lsb) > n: + lsb -= 1 + + cur_cover = 1 << lsb + n -= cur_cover + ret.append( + self.to_ip(num_ip) + f"/{32-lsb}" + ) + num_ip += cur_cover + + return ret + + def to_bin(self, ip): + ret = 0 + for n, w in zip(map(int, ip.split(".")), weights): + ret += n << w + + return ret + + def to_ip(self, bin): + ret = [] + for w in weights: + ret.append( + (bin >> w) & 255 + ) + return ".".join(map(str, ret)) + + def get_lsb(self, n): + lsb = 0 + while (n >> lsb) & 1 == 0: + lsb += 1 + # n >>= lsb # error + return lsb + + +if __name__ == "__main__": + assert Solution().ipToCIDR("60.166.253.147", 12) == ["60.166.253.147/32","60.166.253.148/30","60.166.253.152/30","60.166.253.156/31","60.166.253.158/32"] + assert Solution().ipToCIDR("255.0.0.7", 10) == ["255.0.0.7/32","255.0.0.8/29","255.0.0.16/32"] From 25f7ea7672d7a075dc6ee8f97fdbd93904b4bf96 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 15 May 2019 15:25:42 -0700 Subject: [PATCH 303/344] 336 Palindrome Pairs --- 336 Palindrome Pairs.py | 98 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 336 Palindrome Pairs.py diff --git a/336 Palindrome Pairs.py b/336 Palindrome Pairs.py new file mode 100644 index 0000000..76106b8 --- /dev/null +++ b/336 Palindrome Pairs.py @@ -0,0 +1,98 @@ +#!/usr/bin/python3 +""" +Given a list of unique words, find all pairs of distinct indices (i, j) in the +given list, so that the concatenation of the two words, i.e. words[i] + words[j] +is a palindrome. + +Example 1: + +Input: ["abcd","dcba","lls","s","sssll"] +Output: [[0,1],[1,0],[3,2],[2,4]] +Explanation: The palindromes are ["dcbaabcd","abcddcba","slls","llssssll"] +Example 2: + +Input: ["bat","tab","cat"] +Output: [[0,1],[1,0]] +Explanation: The palindromes are ["battab","tabbat"] +""" +from typing import List +from collections import defaultdict + + +class TrieNode: + def __init__(self): + self.pali_prefix_idxes = [] # suffix ends, prefix pali + self.word_idx = None + self.children = defaultdict(TrieNode) + + +class Solution: + def palindromePairs(self, words: List[str]) -> List[List[int]]: + """ + Brute force, i, j and then check palindrom + O(N^2 * L) + + Reverse the str, and then check O(N * L). Does it work actually? + Check: map str -> idx + + |---s1---|---s2--| |---s1---|-s2-| |-s1-|---s2---| + Need to check whether part of the str is palindrome. + Part of str -> Trie. + How to check part of the str. Useful + + Better way of checking palindrome? Infamouse Manacher + + word_i | word_j + abc pppp | cba + abc | pppp cba + + If palindrome suffix in work_i, we only need to check the "abc" against word_j + Similarly for palindrome prefix in word_j + + Construct Trie for word_j reversely, since word_j is being checked + """ + root = TrieNode() + for idx, w in enumerate(words): + cur = root + for i in range(len(w) - 1, -1, -1): + # cur.children[w[i]] # error, pre-forward unable to handle empty str + if self.is_palindrome(w, 0, i + 1): # exclude w[i] rather than include + cur.pali_prefix_idxes.append(idx) + + cur = cur.children[w[i]] + + cur.pali_prefix_idxes.append(idx) + cur.word_idx = idx # word ends + + ret = [] + for idx, w in enumerate(words): + cur = root + for i in range(len(w)): + # cur.children.get(w[i], None) # error, pre-forward unable to handle empty str + if self.is_palindrome(w, i, len(w)) and cur.word_idx is not None and cur.word_idx != idx: + ret.append([idx, cur.word_idx]) + + cur = cur.children.get(w[i], None) + if cur is None: + break + else: + for idx_j in cur.pali_prefix_idxes: + if idx != idx_j: + ret.append([idx, idx_j]) + + return ret + + def is_palindrome(self, w, lo, hi): + i = lo + j = hi - 1 + while i < j: + if w[i] != w[j]: + return False + i += 1 + j -= 1 + return True + + +if __name__ == "__main__": + assert Solution().palindromePairs(["a", ""]) == [[0,1],[1,0]] + assert Solution().palindromePairs(["abcd","dcba","lls","s","sssll"]) == [[0,1],[1,0],[2,4],[3,2]] From 44e277514145e7757472663fa442906dc46f031b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 15 May 2019 17:21:45 -0700 Subject: [PATCH 304/344] 773 Sliding Puzzle --- 755 Pour Water.py | 155 ++++++++++++++++++++++++++++++++++++++++++ 773 Sliding Puzzle.py | 128 ++++++++++++++++++++++++++++++++++ 2 files changed, 283 insertions(+) create mode 100644 755 Pour Water.py create mode 100644 773 Sliding Puzzle.py diff --git a/755 Pour Water.py b/755 Pour Water.py new file mode 100644 index 0000000..ba6a884 --- /dev/null +++ b/755 Pour Water.py @@ -0,0 +1,155 @@ +#!/usr/bin/python3 +""" +We are given an elevation map, heights[i] representing the height of the terrain +at that index. The width at each index is 1. After V units of water fall at +index K, how much water is at each index? + +Water first drops at index K and rests on top of the highest terrain or water at +that index. Then, it flows according to the following rules: + +If the droplet would eventually fall by moving left, then move left. +Otherwise, if the droplet would eventually fall by moving right, then move right. +Otherwise, rise at it's current position. +Here, "eventually fall" means that the droplet will eventually be at a lower +level if it moves in that direction. Also, "level" means the height of the +terrain plus any water in that column. +We can assume there's infinitely high terrain on the two sides out of bounds of +the array. Also, there could not be partial water being spread out evenly on +more than 1 grid block - each unit of water has to be in exactly one block. + +Example 1: +Input: heights = [2,1,1,2,1,2,2], V = 4, K = 3 +Output: [2,2,2,3,2,2,2] +Explanation: +# # +# # +## # ### +######### + 0123456 <- index + +The first drop of water lands at index K = 3: + +# # +# w # +## # ### +######### + 0123456 + +When moving left or right, the water can only move to the same level or a lower +level. +(By level, we mean the total height of the terrain plus any water in that column.) +Since moving left will eventually make it fall, it moves left. +(A droplet "made to fall" means go to a lower height than it was at previously.) + +# # +# # +## w# ### +######### + 0123456 + +Since moving left will not make it fall, it stays in place. The next droplet +falls: + +# # +# w # +## w# ### +######### + 0123456 + +Since the new droplet moving left will eventually make it fall, it moves left. +Notice that the droplet still preferred to move left, +even though it could move right (and moving right makes it fall quicker.) + +# # +# w # +## w# ### +######### + 0123456 + +# # +# # +##ww# ### +######### + 0123456 + +After those steps, the third droplet falls. +Since moving left would not eventually make it fall, it tries to move right. +Since moving right would eventually make it fall, it moves right. + +# # +# w # +##ww# ### +######### + 0123456 + +# # +# # +##ww#w### +######### + 0123456 + +Finally, the fourth droplet falls. +Since moving left would not eventually make it fall, it tries to move right. +Since moving right would not eventually make it fall, it stays in place: + +# # +# w # +##ww#w### +######### + 0123456 + +The final answer is [2,2,2,3,2,2,2]: + + # + ####### + ####### + 0123456 +Example 2: +Input: heights = [1,2,3,4], V = 2, K = 2 +Output: [2,3,3,4] +Explanation: +The last droplet settles at index 1, since moving further left would not cause +it to eventually fall to a lower height. +Example 3: +Input: heights = [3,1,3], V = 5, K = 1 +Output: [4,4,4] +Note: + +heights will have length in [1, 100] and contain integers in [0, 99]. +V will be in range [0, 2000]. +K will be in range [0, heights.length - 1]. +""" +from typing import List + + +class Solution: + def pourWater(self, heights: List[int], V: int, K: int) -> List[int]: + """ + Simulation? + O(V * L) + """ + for _ in range(V): + s = K + # looking to the left + optimal = s + for i in range(s-1, -1, -1): + if heights[i] <= heights[i+1]: + if heights[i] < heights[optimal]: + optimal = i + else: + break + if optimal == s: + # looking to the right + for i in range(s+1, len(heights)): + if heights[i] <= heights[i-1]: + if heights[i] < heights[optimal]: + optimal = i + else: + break + heights[optimal] += 1 + + return heights + + +if __name__ == "__main__": + assert Solution().pourWater([2,1,1,2,1,2,2], 4, 3) == [2,2,2,3,2,2,2] diff --git a/773 Sliding Puzzle.py b/773 Sliding Puzzle.py new file mode 100644 index 0000000..637e766 --- /dev/null +++ b/773 Sliding Puzzle.py @@ -0,0 +1,128 @@ +#!/usr/bin/python3 +""" +On a 2x3 board, there are 5 tiles represented by the integers 1 through 5, and +an empty square represented by 0. + +A move consists of choosing 0 and a 4-directionally adjacent number and swapping +it. + +The state of the board is solved if and only if the board is [[1,2,3],[4,5,0]]. + +Given a puzzle board, return the least number of moves required so that the +state of the board is solved. If it is impossible for the state of the board to +be solved, return -1. + +Examples: + +Input: board = [[1,2,3],[4,0,5]] +Output: 1 +Explanation: Swap the 0 and the 5 in one move. +Input: board = [[1,2,3],[5,4,0]] +Output: -1 +Explanation: No number of moves will make the board solved. +Input: board = [[4,1,2],[5,0,3]] +Output: 5 +Explanation: 5 is the smallest number of moves that solves the board. +An example path: +After move 0: [[4,1,2],[5,0,3]] +After move 1: [[4,1,2],[0,5,3]] +After move 2: [[0,1,2],[4,5,3]] +After move 3: [[1,0,2],[4,5,3]] +After move 4: [[1,2,0],[4,5,3]] +After move 5: [[1,2,3],[4,5,0]] +Input: board = [[3,2,4],[1,5,0]] +Output: 14 +Note: + +board will be a 2 x 3 array as described above. +board[i][j] will be a permutation of [0, 1, 2, 3, 4, 5]. +""" +from typing import List +from collections import defaultdict +from copy import deepcopy +import heapq + + +final_pos = { + 1: (0, 0), + 2: (0, 1), + 3: (0, 2), + 4: (1, 0), + 5: (1, 1), + 0: (1, 2), +} + +dirs = ( + (0, -1), + (0, 1), + (-1, 0), + (1, 0), +) + + +class Solution: + def slidingPuzzle(self, board: List[List[int]]) -> int: + """ + BFS + visited + + => A* + priority = current_dist + heuristic_dist + + Chain the matrix into 1d array. N = R * C + Complexity O(N * N!) + There are O(N!) possible board states. O(N) is the time to scan the board for the operations in the loop. + """ + visited = defaultdict(bool) + m, n = len(board), len(board[0]) + q = [(self.heuristic_dist(board) + 0, 0, board)] + target = [ + [1, 2, 3], + [4, 5, 0], + ] + while q: + heu, cur_dist, board = heapq.heappop(q) + visited[self.ser(board)] = True + if board == target: + return cur_dist + + cur_dist += 1 + i, j = self.zero_pos(board) + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < m and 0 <= J < n: + B = deepcopy(board) # need a copy in the queue + B[I][J], B[i][j] = B[i][j], B[I][J] + if not visited[self.ser(B)]: + heapq.heappush(q, (self.heuristic_dist(B) + cur_dist, cur_dist, B)) + return -1 + + def zero_pos(self, board): + for i, row in enumerate(board): + for j, v in enumerate(row): + if v == 0: + return i, j + raise + + def heuristic_dist(self, board): + """ + manhattan distance + """ + ret = 0 + for i, row in enumerate(board): + for j, v in enumerate(row): + if v != 0: + I, J = final_pos[v] + ret += abs(i - I) + abs(j - J) + return ret + + def ser(self, board): + return tuple( + tuple(row) + for row in board + ) + + +if __name__ == "__main__": + assert Solution().slidingPuzzle([[1,2,3],[4,0,5]]) == 1 + assert Solution().slidingPuzzle([[1,2,3],[5,4,0]]) == -1 From 7d562fe717c186177352e567655d0491851514e4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 15 May 2019 18:11:36 -0700 Subject: [PATCH 305/344] 1017 Convert to Base -2 --- 1017 Convert to Base -2.py | 67 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 1017 Convert to Base -2.py diff --git a/1017 Convert to Base -2.py b/1017 Convert to Base -2.py new file mode 100644 index 0000000..434b580 --- /dev/null +++ b/1017 Convert to Base -2.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +Given a number N, return a string consisting of "0"s and "1"s that represents +its value in base -2 (negative two). + +The returned string must have no leading zeroes, unless the string is "0". + + + +Example 1: + +Input: 2 +Output: "110" +Explantion: (-2) ^ 2 + (-2) ^ 1 = 2 +Example 2: + +Input: 3 +Output: "111" +Explantion: (-2) ^ 2 + (-2) ^ 1 + (-2) ^ 0 = 3 +Example 3: + +Input: 4 +Output: "100" +Explantion: (-2) ^ 2 = 4 + + +Note: +0 <= N <= 10^9 +""" +from collections import deque + + +class Solution: + def baseNeg2(self, N: int) -> str: + """ + % -2, // -2 ? not really + + alternating + + + 3 + (-2) ^ 2 + (-2) ^ 1 + (-2) ^ 0, LSB set + minus reminder, divide by -2 + (-2) ^ 1 + (-2) ^ 0, LSB set + minus reminder, divide by -2 + (-2) ^ 0, LSB set + + 4 + (-2) ^ 2 + 0 ^ 1 + 0 ^ 0, LSB not set + minus reminder, divide by -2 + (-2) ^ 1 + 0 ^ 0, LSB not set + minus reminder, divide by -2 + (-2) ^ 0, LSB set + """ + ret = deque() + while N != 0: + r = N % 2 # r is the range of 0 and 2 + ret.appendleft(r) + N -= r + N //= -2 + + return "".join(map(str, ret)) or "0" + + +if __name__ == "__main__": + assert Solution().baseNeg2(3) == "111" + assert Solution().baseNeg2(4) == "100" From 27e906b605a25a7a812381ba38f8d368260b8b69 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 15 May 2019 21:15:11 -0700 Subject: [PATCH 306/344] 759 Employee Free Time --- 759 Employee Free Time.py | 151 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 759 Employee Free Time.py diff --git a/759 Employee Free Time.py b/759 Employee Free Time.py new file mode 100644 index 0000000..463fbab --- /dev/null +++ b/759 Employee Free Time.py @@ -0,0 +1,151 @@ +#!/usr/bin/python3 +""" +We are given a list schedule of employees, which represents the working time for +each employee. + +Each employee has a list of non-overlapping Intervals, and these intervals are +in sorted order. + +Return the list of finite intervals representing common, positive-length free +time for all employees, also in sorted order. + +Example 1: +Input: schedule = [[[1,2],[5,6]],[[1,3]],[[4,10]]] +Output: [[3,4]] +Explanation: +There are a total of three employees, and all common +free time intervals would be [-inf, 1], [3, 4], [10, inf]. +We discard any intervals that contain inf as they aren't finite. + + +Example 2: +Input: schedule = [[[1,3],[6,7]],[[2,4]],[[2,5],[9,12]]] +Output: [[5,6],[7,9]] + + +(Even though we are representing Intervals in the form [x, y], the objects +inside are Intervals, not lists or arrays. For example, schedule[0][0].start = 1 +, schedule[0][0].end = 2, and schedule[0][0][0] is not defined.) + +Also, we wouldn't include intervals like [5, 5] in our answer, as they have zero +length. + +Note: + +schedule and schedule[i] are lists with lengths in range [1, 50]. +0 <= schedule[i].start < schedule[i].end <= 10^8. +""" +from typing import List +import heapq + + +S = 0 +E = 1 + + +class Solution: + def employeeFreeTime(self, schedule: List[List[List[int]]]) -> List[List[int]]: + """ + Method 1 + Looking at the head of each list through iterator + Merge interval of heads, need to sort, then use heap + After merge, find the open interval + + No need to merge, find the max end time, and compare to the min start time + + Method 2 + Better algorithm to find the open interval + [s, e], we can think of this as two events: balance++ when time = s, and + balance-- when time = e. We want to know the regions where balance == 0. + + Similar to meeting rooms II + """ + max_end = min( + itv[E] + for itvs in schedule + for itv in itvs + ) + q = [] + for i, itvs in enumerate(schedule): + # head + j = 0 + itv = itvs[j] + heapq.heappush(q, (itv[S], i, j)) + + ret = [] + while q: + _, i, j = heapq.heappop(q) + itv = schedule[i][j] + if max_end < itv[S]: + ret.append([max_end, itv[S]]) + + max_end = max(max_end, itv[E]) + + # next + j += 1 + if j < len(schedule[i]): + itv = schedule[i][j] + heapq.heappush(q, (itv[S], i, j)) + + return ret + + def employeeFreeTime(self, schedule: List[List[List[int]]]) -> List[List[int]]: + """ + Method 2 + """ + # flatten the nested list + lst = [] + for itvs in schedule: + for itv in itvs: + lst.append([itv[S], S]) + lst.append([itv[E], E]) + + lst.sort() + count = 0 + prev = None + ret = [] + for t, flag in lst: + if count == 0 and prev: + ret.append([prev, t]) + + if flag == S: + count += 1 + else: + prev = t + count -= 1 + + return ret + + def employeeFreeTime_error(self, schedule: List[List[List[int]]]) -> List[List[int]]: + """ + Cannot store iterator in the heap to compare + use index instead + """ + schedules = list(map(iter, schedule)) + max_end = min( + itv[E] + for emp in schedule + for itv in emp + ) + q = [] + for emp_iter in schedules: + itv = next(emp_iter, None) + if itv: + heapq.heappush(q, (itv[S], itv, emp_iter)) + + ret = [] + while q: + _, itv, emp_iter = heapq.heappop(q) + if max_end < itv[S]: + ret.append([max_end, itv[S]]) + max_end = max(max_end, itv[E]) + itv = next(emp_iter, None) + if itv: + heapq.heappush(q, (itv[S], itv, emp_iter)) + + return ret + + +if __name__ == "__main__": + assert Solution().employeeFreeTime([[[1,2],[5,6]],[[1,3]],[[4,10]]]) == [[3,4]] + assert Solution().employeeFreeTime([[[4,16],[31,36],[42,50],[80,83],[95,96]],[[4,13],[14,19],[37,53],[64,66],[85,89]],[[17,24],[38,39],[49,51],[62,67],[79,81]],[[9,15],[17,24],[45,63],[65,68],[87,88]],[[17,33],[39,41],[43,57],[58,63],[70,84]]]) == [[36, 37], [68, 70], [84, 85], [89, 95]] From 9b4de894faf64ca609884ff9b1d53ab0185f87c3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 16 May 2019 22:22:09 -0700 Subject: [PATCH 307/344] 269 Alien Dictionary --- 269 Alien Dictionary py3.py | 96 +++++++++++++++++++++++++++++++++++++ 269 Alien Dictionary.py | 49 +++++++++++++++++-- 348 Design Tic-Tac-Toe.py | 73 +++++++++++++++++++++++----- 3 files changed, 202 insertions(+), 16 deletions(-) create mode 100644 269 Alien Dictionary py3.py diff --git a/269 Alien Dictionary py3.py b/269 Alien Dictionary py3.py new file mode 100644 index 0000000..10a0c41 --- /dev/null +++ b/269 Alien Dictionary py3.py @@ -0,0 +1,96 @@ +""" +There is a new alien language which uses the latin alphabet. However, the order +among letters are unknown to you. You receive a list of non-empty words from the +dictionary, where words are sorted lexicographically by the rules of this new +language. Derive the order of letters in this language. + +Example 1: + +Input: +[ + "wrt", + "wrf", + "er", + "ett", + "rftt" +] + +Output: "wertf" +Example 2: + +Input: +[ + "z", + "x" +] + +Output: "zx" +Example 3: + +Input: +[ + "z", + "x", + "z" +] + +Output: "" + +Explanation: The order is invalid, so return "". +""" +from typing import List +from collections import defaultdict, deque + + +class Solution(object): + def alienOrder(self, words: List[str]) -> str: + G = self.construct_graph(words) + visited = defaultdict(int) # 0 not visited, 1 visiting, 2 visted + ret = deque() + for u in G.keys(): + if visited[u] == 0: + if not self.topo_dfs(G, u, visited, ret): + return "" + + return "".join(ret) + + def construct_graph(self, words): + G = defaultdict(list) + # need to initialize, consider test case ["z", "z"] + for w in words: # error + for c in w: + G[c] + + for i in range(len(words) - 1): # compare word_i and word_{i+1} + for c1, c2 in zip(words[i], words[i+1]): + if c1 != c2: + G[c1].append(c2) + break # need to break for lexical order + + return G + + def topo_dfs(self, G, u, visited, ret): + """ + Topological sort + G = defaultdict(list) + visited = defaultdict(int) # 0 not visited, 1 visiteding, 2 visted + + pre-condition: u is not visited (0) + """ + visited[u] = 1 + for nbr in G[u]: + if visited[nbr] == 1: + return False + if visited[nbr] == 0: + if not self.topo_dfs(G, nbr, visited, ret): + return False + + visited[u] = 2 + ret.appendleft(u) # visit larger first + return True + + +if __name__ == "__main__": + lst = ["ze", "yf", "xd", "wd", "vd", "ua", "tt", "sz", "rd", "qd", "pz", "op", "nw", "mt", "ln", "ko", "jm", "il", + "ho", "gk", "fa", "ed", "dg", "ct", "bb", "ba"] + assert Solution().alienOrder(lst) == "zyxwvutsrqponmlkjihgfedcba" diff --git a/269 Alien Dictionary.py b/269 Alien Dictionary.py index b95e041..f317fcd 100644 --- a/269 Alien Dictionary.py +++ b/269 Alien Dictionary.py @@ -1,5 +1,42 @@ """ -Premium Question +There is a new alien language which uses the latin alphabet. However, the order +among letters are unknown to you. You receive a list of non-empty words from the +dictionary, where words are sorted lexicographically by the rules of this new +language. Derive the order of letters in this language. + +Example 1: + +Input: +[ + "wrt", + "wrf", + "er", + "ett", + "rftt" +] + +Output: "wertf" +Example 2: + +Input: +[ + "z", + "x" +] + +Output: "zx" +Example 3: + +Input: +[ + "z", + "x", + "z" +] + +Output: "" + +Explanation: The order is invalid, so return "". """ from collections import defaultdict @@ -25,11 +62,15 @@ def alienOrder(self, words): def construct_graph(self, words): V = defaultdict(list) + # need to initialize, consider test case ["z", "z"] + for w in words: # pitfall + for c in w: + V[c] for i in xrange(len(words) - 1): # compare word_i and word_{i+1} for j in xrange(min(len(words[i]), len(words[i+1]))): if words[i][j] != words[i+1][j]: V[words[i][j]].append(words[i+1][j]) - break # need to break + break # need to break for lexical order return V @@ -53,8 +94,8 @@ def topo_dfs(self, V, v, visited, pathset, ret): return False pathset.remove(v) - visited.add(v) - ret.append(v) + visited.add(v) # add visited is in the end rather than at the begining + ret.append(v) # append after lower values return True def construct_graph_tedious(self, words, up, down, ptr, V): diff --git a/348 Design Tic-Tac-Toe.py b/348 Design Tic-Tac-Toe.py index 0b1bb31..bd1241b 100644 --- a/348 Design Tic-Tac-Toe.py +++ b/348 Design Tic-Tac-Toe.py @@ -1,5 +1,53 @@ """ -Premium Question +Design a Tic-tac-toe game that is played between two players on a n x n grid. + +You may assume the following rules: + +A move is guaranteed to be valid and is placed on an empty block. +Once a winning condition is reached, no more moves is allowed. +A player who succeeds in placing n of their marks in a horizontal, vertical, or +diagonal row wins the game. +Example: +Given n = 3, assume that player 1 is "X" and player 2 is "O" in the board. + +TicTacToe toe = new TicTacToe(3); + +toe.move(0, 0, 1); -> Returns 0 (no one wins) +|X| | | +| | | | // Player 1 makes a move at (0, 0). +| | | | + +toe.move(0, 2, 2); -> Returns 0 (no one wins) +|X| |O| +| | | | // Player 2 makes a move at (0, 2). +| | | | + +toe.move(2, 2, 1); -> Returns 0 (no one wins) +|X| |O| +| | | | // Player 1 makes a move at (2, 2). +| | |X| + +toe.move(1, 1, 2); -> Returns 0 (no one wins) +|X| |O| +| |O| | // Player 2 makes a move at (1, 1). +| | |X| + +toe.move(2, 0, 1); -> Returns 0 (no one wins) +|X| |O| +| |O| | // Player 1 makes a move at (2, 0). +|X| |X| + +toe.move(1, 0, 2); -> Returns 0 (no one wins) +|X| |O| +|O|O| | // Player 2 makes a move at (1, 0). +|X| |X| + +toe.move(2, 1, 1); -> Returns 1 (player 1 wins) +|X| |O| +|O|O| | // Player 1 makes a move at (2, 1). +|X|X|X| +Follow up: +Could you do better than O(n2) per move() operation? """ __author__ = 'Daniel' @@ -11,10 +59,10 @@ def __init__(self, n): :type n: int """ self.n = n - self.rows = [0 for _ in xrange(n)] - self.cols = [0 for _ in xrange(n)] - self.diag0 = 0 - self.diag1 = 0 + self.rows_count = [0 for _ in xrange(n)] + self.cols_count = [0 for _ in xrange(n)] + self.diag_count = 0 + self.diag_inv_count = 0 def move(self, row, col, player): """ @@ -35,19 +83,20 @@ def move(self, row, col, player): :rtype: int """ delta = -1 if player == 1 else 1 - self.cols[col] += delta - self.rows[row] += delta + self.cols_count[col] += delta + self.rows_count[row] += delta if col == row: - self.diag0 += delta + self.diag_count += delta if col + row == self.n - 1: - self.diag1 += delta + self.diag_inv_count += delta - is_win = lambda x: delta * x == self.n - if any(map(is_win, [self.rows[row], self.cols[col], self.diag0, self.diag1])): + # since winning condition is taking up the entire row or col, the row or col must be consecutive + is_win = lambda count: delta * count == self.n + if any(map(is_win, [self.rows_count[row], self.cols_count[col], self.diag_count, self.diag_inv_count])): return player return 0 # Your TicTacToe object will be instantiated and called as such: # obj = TicTacToe(n) -# param_1 = obj.move(row,col,player) \ No newline at end of file +# param_1 = obj.move(row,col,player) From 536614e6951bfcac0e48573d6c2fb02ec582d574 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 16 May 2019 22:34:13 -0700 Subject: [PATCH 308/344] 021 Generate Parentheses --- 021 Generate Parentheses py3.py | 34 +++++++++++++++++++++++++++++++++ 021 Generate Parentheses.py | 15 ++++++++------- 269 Alien Dictionary.py | 1 + 3 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 021 Generate Parentheses py3.py diff --git a/021 Generate Parentheses py3.py b/021 Generate Parentheses py3.py new file mode 100644 index 0000000..c55b850 --- /dev/null +++ b/021 Generate Parentheses py3.py @@ -0,0 +1,34 @@ +#!/usr/bin/python3 +""" +Given n pairs of parentheses, write a function to generate all combinations of +well-formed parentheses. + +For example, given n = 3, a solution set is: + +[ + "((()))", + "(()())", + "(())()", + "()(())", + "()()()" +] +""" +from typing import List + + +class Solution: + def generateParenthesis(self, n: int) -> List[str]: + """ + Method 1 - backtracking + Method 2 - dp + Let F[n] be the list of parentheses at length 2n + """ + F: List[List[str]] = [[] for _ in range(n + 1)] + F[0].append("") + for i in range(1, n+1): + for j in range(i): + for s1 in F[j]: + for s2 in F[i-j-1]: + F[i].append(f"({s1}){s2}") + + return F[n] diff --git a/021 Generate Parentheses.py b/021 Generate Parentheses.py index ac64cc8..32d85db 100644 --- a/021 Generate Parentheses.py +++ b/021 Generate Parentheses.py @@ -25,16 +25,17 @@ def generateParenthesisDfs(self, result, cur, left, right): :param left: number of left parenthesis remaining :param right: number of right parenthesis remaining """ - # trivial - if left==0 and right==0: + if left == 0 and right == 0: result.append(cur) return + # add left parenthesis - if left>0: - self.generateParenthesisDfs(result, cur+"(", left-1, right) + if left > 0: + self.generateParenthesisDfs(result, cur + "(", left - 1, right) # add right parenthesis - if right>left: - self.generateParenthesisDfs(result, cur+")", left, right-1) + if right > left: + self.generateParenthesisDfs(result, cur + ")", left, right - 1) + if __name__=="__main__": - assert Solution().generateParenthesis(3)==['((()))', '(()())', '(())()', '()(())', '()()()'] \ No newline at end of file + assert Solution().generateParenthesis(3)==['((()))', '(()())', '(())()', '()(())', '()()()'] diff --git a/269 Alien Dictionary.py b/269 Alien Dictionary.py index f317fcd..3945a78 100644 --- a/269 Alien Dictionary.py +++ b/269 Alien Dictionary.py @@ -1,3 +1,4 @@ +#!/usr/bin/python3 """ There is a new alien language which uses the latin alphabet. However, the order among letters are unknown to you. You receive a list of non-empty words from the From 9ff06d40ffa908652d08e211ffa9e006d308e5e9 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 19 May 2019 14:27:12 -0700 Subject: [PATCH 309/344] 410 Split Array Largest Sum --- 410 Split Array Largest Sum.py | 152 +++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 410 Split Array Largest Sum.py diff --git a/410 Split Array Largest Sum.py b/410 Split Array Largest Sum.py new file mode 100644 index 0000000..a258f4b --- /dev/null +++ b/410 Split Array Largest Sum.py @@ -0,0 +1,152 @@ +#!/usr/bin/python3 +""" +Given an array which consists of non-negative integers and an integer m, you can +split the array into m non-empty continuous subarrays. Write an algorithm to +minimize the largest sum among these m subarrays. + +Note: +If n is the length of array, assume the following constraints are satisfied: + +1 ≤ n ≤ 1000 +1 ≤ m ≤ min(50, n) +Examples: + +Input: +nums = [7,2,5,10,8] +m = 2 + +Output: +18 + +Explanation: +There are four ways to split nums into two subarrays. +The best way is to split it into [7,2,5] and [10,8], +where the largest sum among the two subarrays is only 18. +""" +from typing import List +from functools import lru_cache + + +class SolutionDP: + def splitArray(self, nums: List[int], m: int) -> int: + """ + non-aftereffect, dp + Let F[l][k] be the minimized max sum in nums[:l] with k parts + F[l][k] = max(F[j][k-1], sum(nums[j:l])), minimize over j + """ + n = len(nums) + sums = [0] + for e in nums: + sums.append(sums[-1] + e) + + F = [[float("inf") for _ in range(m + 1)] for _ in range(n + 1)] + for l in range(1, n + 1): + F[l][1] = sums[l] - sums[0] + # or F[0][0] = 0 + + for l in range(1, n + 1): + for k in range(1, m + 1): + for j in range(l): + F[l][k] = min( + F[l][k], max(F[j][k-1], sums[l] - sums[j]) + ) + + return F[n][m] + + +class Solution: + def splitArray(self, nums: List[int], m: int) -> int: + """ + Binary search over the subarray sum values + """ + lo = max(nums) + hi = sum(nums) + 1 + ret = hi + while lo < hi: + mid = (lo + hi) // 2 + cnt = 1 # pitfall, initial is 1 (the 1st running sum) + cur_sum = 0 + for e in nums: + if cur_sum + e > mid: + cnt += 1 + cur_sum = e + else: + cur_sum += e + + if cnt <= m: + ret = min(ret, mid) # pitfall. Condition satisfied + hi = mid + else: + lo = mid + 1 + + return ret + + +class SolutionTLE2: + def __init__(self): + self.sums = [0] + + def splitArray(self, nums: List[int], m: int) -> int: + """ + memoization with 1 less param + """ + for n in nums: + self.sums.append(self.sums[-1] + n) + + ret = self.dfs(len(nums), m) + return ret + + @lru_cache(maxsize=None) + def dfs(self, hi, m): + """ + j break the nums[:hi] into left and right part + """ + if m == 1: + return self.sums[hi] - self.sums[0] + + mini = float("inf") + for j in range(hi): + right = self.sums[hi] - self.sums[j] + left = self.dfs(j, m - 1) + # minimize the max + mini = min(mini, max(left, right)) + + return mini + + +class SolutionTLE: + def __init__(self): + self.sums = [0] + + def splitArray(self, nums: List[int], m: int) -> int: + """ + Minimize the largest subarray sum + + backtracking + memoization + """ + for n in nums: + self.sums.append(self.sums[-1] + n) + ret = self.dfs(tuple(nums), 0, len(nums), m) + return ret + + @lru_cache(maxsize=None) + def dfs(self, nums, lo, hi, m): + """ + j break the nums[lo:hi] into left and right part + """ + if m == 1: + return self.sums[hi] - self.sums[lo] + + mini = float("inf") + for j in range(lo, hi): + left = self.sums[j] - self.sums[lo] + right = self.dfs(nums, j, hi, m - 1) + # minimize the max + mini = min(mini, max(left, right)) + + return mini + + +if __name__ == "__main__": + assert Solution().splitArray([1, 4, 4], 3) == 4 + assert Solution().splitArray([7,2,5,10,8], 2) == 18 From 5f0d199691098a9b4eb760da6e8d3766e860b0ed Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 19 May 2019 16:40:29 -0700 Subject: [PATCH 310/344] 756 Pyramid Transition Matrix --- 756 Pyramid Transition Matrix.py | 108 +++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 756 Pyramid Transition Matrix.py diff --git a/756 Pyramid Transition Matrix.py b/756 Pyramid Transition Matrix.py new file mode 100644 index 0000000..b940516 --- /dev/null +++ b/756 Pyramid Transition Matrix.py @@ -0,0 +1,108 @@ +#!/usr/bin/python3 +""" +We are stacking blocks to form a pyramid. Each block has a color which is a one +letter string. + +We are allowed to place any color block C on top of two adjacent blocks of +colors A and B, if and only if ABC is an allowed triple. + +We start with a bottom row of bottom, represented as a single string. We also +start with a list of allowed triples allowed. Each allowed triple is represented +as a string of length 3. + +Return true if we can build the pyramid all the way to the top, otherwise false. + +Example 1: + +Input: bottom = "BCD", allowed = ["BCG", "CDE", "GEA", "FFF"] +Output: true +Explanation: +We can stack the pyramid like this: + A + / \ + G E + / \ / \ +B C D + +We are allowed to place G on top of B and C because BCG is an allowed triple. +Similarly, we can place E on top of C and D, then A on top of G and E. + + +Example 2: + +Input: bottom = "AABA", allowed = ["AAA", "AAB", "ABA", "ABB", "BAC"] +Output: false +Explanation: +We can't stack the pyramid to the top. +Note that there could be allowed triples (A, B, C) and (A, B, D) with C != D. + + +Note: + +bottom will be a string with length in range [2, 8]. +allowed will have length in range [0, 200]. +Letters in all strings will be chosen from the set {'A', 'B', 'C', 'D', 'E', +'F', 'G'}. +""" +import itertools +from typing import List +from collections import defaultdict + + +class Solution: + def pyramidTransition(self, bottom: str, allowed: List[str]) -> bool: + """ + Need search, since multiple placements are possible + The order of allowed matters + """ + T = defaultdict(set) # transition matrix + for a, b, c in allowed: + T[a, b].add(c) + + return self.dfs(T, bottom) + + def dfs(self, T, level) -> bool: + if len(level) == 1: + return True + + # for nxt_level in self.gen_nxt_level(T, level, 0): + for nxt_level in itertools.product( + *[T[a, b] for a, b in zip(level, level[1:])] + ): + if self.dfs(T, nxt_level): + return True + + return False + + def gen_nxt_level(self, T, level, lo): + """ + equiv to itertools.product - nested for-loops in a generator expression + Cartesian product + """ + if lo + 1 >= len(level): + yield "" + return + + for head in T[level[lo], level[lo + 1]]: + for tail in self.gen_nxt_level(T, level, lo + 1): + yield head + tail + + + def dfs_deep(self, T, level, lo, nxt_level) -> bool: + if lo + 1 == len(level): + return True + + for nxt in T[level[lo], level[lo + 1]]: + nxt_level.append(nxt) + if self.dfs(T, level, lo + 1, nxt_level): + # Too deep - check till top + if self.dfs(T, nxt_level, 0, []): + return True + nxt_level.pop() + + return False + + +if __name__ == "__main__": + assert Solution().pyramidTransition("BCD", ["BCG", "CDE", "GEA", "FFF"]) == True + assert Solution().pyramidTransition("AABA", ["AAA", "AAB", "ABA", "ABB", "BAC"]) == False From c52cfa13582a3da3eb60131daab6582e739ff699 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 22 May 2019 00:11:14 -0700 Subject: [PATCH 311/344] 815 Bus Routes --- 131 Palindrome Partitioning.py | 6 +- 815 Bus Routes.py | 110 +++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 815 Bus Routes.py diff --git a/131 Palindrome Partitioning.py b/131 Palindrome Partitioning.py index a869452..3d80eaa 100644 --- a/131 Palindrome Partitioning.py +++ b/131 Palindrome Partitioning.py @@ -35,8 +35,8 @@ def get_partition(self, seq, cur, result): def is_palindrome(self, s): # O(n) - # return s==reversed(s) # error, need to use ''.join(reversed(s)) - return s==s[::-1] + # return s == reversed(s) # error, need to use ''.join(reversed(s)) + return s == s[::-1] if __name__=="__main__": - assert Solution().partition("aab")==[['a', 'a', 'b'], ['aa', 'b']] \ No newline at end of file + assert Solution().partition("aab")==[['a', 'a', 'b'], ['aa', 'b']] diff --git a/815 Bus Routes.py b/815 Bus Routes.py new file mode 100644 index 0000000..8209712 --- /dev/null +++ b/815 Bus Routes.py @@ -0,0 +1,110 @@ +#!/usr/bin/python3 +""" +We have a list of bus routes. Each routes[i] is a bus route that the i-th bus +repeats forever. For example if routes[0] = [1, 5, 7], this means that the first +bus (0-th indexed) travels in the sequence 1->5->7->1->5->7->1->... forever. + +We start at bus stop S (initially not on a bus), and we want to go to bus stop +T. Travelling by buses only, what is the least number of buses we must take to +reach our destination? Return -1 if it is not possible. + +Example: +Input: +routes = [[1, 2, 7], [3, 6, 7]] +S = 1 +T = 6 +Output: 2 +Explanation: +The best strategy is take the first bus to the bus stop 7, then take the second +bus to the bus stop 6. + +Note: +1 <= routes.length <= 500. +1 <= routes[i].length <= 500. +0 <= routes[i][j] < 10 ^ 6. +""" +from typing import List +from collections import defaultdict + + +class Solution: + def numBusesToDestination(self, routes: List[List[int]], S: int, T: int) -> int: + """ + BFS + bus based nodes rather than stop based nodes + + BFS = O(|V| + |E|) = O(N + N^2), where N is number of routes + Construction = O (N^2 * S), where S is number of stops + """ + if S == T: + return 0 + + routes = [set(e) for e in routes] + G = defaultdict(set) + for i in range(len(routes)): + for j in range(i + 1, len(routes)): + stops_1, stops_2 = routes[i], routes[j] # bus represented by stops + for stop in stops_1: # any(stop in stops_2 for stop in stops_1) + if stop in stops_2: + G[i].add(j) + G[j].add(i) + break + + q = [i for i, stops in enumerate(routes) if S in stops] + target_set = set([i for i, stops in enumerate(routes) if T in stops]) + visited = defaultdict(bool) + for i in q: + visited[i] = True + step = 1 + while q: + cur_q = [] + for e in q: + if e in target_set: + return step + for nbr in G[e]: + if not visited[nbr]: + visited[nbr] = True + cur_q.append(nbr) + + step += 1 + q = cur_q + + return -1 + + def numBusesToDestination_TLE(self, routes: List[List[int]], S: int, T: int) -> int: + """ + BFS + Lest number of buses rather than bus stops + + Connect stops within in bus use one edge in G + """ + G = defaultdict(set) + for stops in routes: + for i in range(len(stops)): + for j in range(i + 1, len(stops)): + u, v = stops[i], stops[j] + G[u].add(v) + G[v].add(u) + + q = [S] + step = 0 + visited = defaultdict(bool) + visited[S] = True # avoid add duplicate + while q: + cur_q = [] + for e in q: + if e == T: + return step + for nbr in G[e]: + if not visited[nbr]: + visited[nbr] = True + cur_q.append(nbr) + + step += 1 + q = cur_q + + return -1 + + +if __name__ == "__main__": + assert Solution().numBusesToDestination([[1, 2, 7], [3, 6, 7]], 1, 6) == 2 From 782f532b5b09e97d17f7292b774ee53b8a516b17 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 13 Jun 2019 22:26:17 -0700 Subject: [PATCH 312/344] 187 --- 187 Repeated DNA Sequences py3.py | 30 ++++++++++++++++++++++++++++++ 187 Repeated DNA Sequences.py | 19 ++++++++++--------- 518 Coin Change 2.py | 1 + 3 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 187 Repeated DNA Sequences py3.py diff --git a/187 Repeated DNA Sequences py3.py b/187 Repeated DNA Sequences py3.py new file mode 100644 index 0000000..358c318 --- /dev/null +++ b/187 Repeated DNA Sequences py3.py @@ -0,0 +1,30 @@ +#!/usr/bin/python3 +""" +All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, +for example: "ACGAATTCCG". When studying DNA, it is sometimes useful to +identify repeated sequences within the DNA. + +Write a function to find all the 10-letter-long sequences (substrings) that +occur more than once in a DNA molecule. + +Example: + +Input: s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT" + +Output: ["AAAAACCCCC", "CCCCCAAAAA"] +""" +from typing import List + + +class Solution: + def findRepeatedDnaSequences(self, s: str) -> List[str]: + ret = set() + seen = set() + for i in range(len(s) - 10 + 1): + sub = s[i:i+10] + if sub in seen and sub not in ret: + ret.add(sub) + else: + seen.add(sub) + + return list(ret) diff --git a/187 Repeated DNA Sequences.py b/187 Repeated DNA Sequences.py index b0e61e3..48f128f 100644 --- a/187 Repeated DNA Sequences.py +++ b/187 Repeated DNA Sequences.py @@ -19,6 +19,8 @@ def findRepeatedDnaSequences(self, s): """ Limited space of possible values --> rewrite hash function + Rolling hash + "A": 0 (00) "C": 1 (01) "G": 2 (10) @@ -32,26 +34,25 @@ def findRepeatedDnaSequences(self, s): s = map(self.mapping, list(s)) h = set() - added = set() - ret = [] + # in_ret = set() + ret = set() cur = 0 for i in xrange(10): cur <<= 2 cur &= 0xFFFFF cur += s[i] - h.add(cur) + for i in xrange(10, len(s)): cur <<= 2 - cur &= 0xFFFFF + cur &= 0xFFFFF # 10 * 2 = 20 position cur += s[i] - if cur in h and cur not in added: - ret.append(self.decode(cur)) - added.add(cur) + if cur in h and cur not in ret: + ret.add(cur) else: h.add(cur) - return ret + return map(self.decode, ret) def decode(self, s): dic = { @@ -78,4 +79,4 @@ def mapping(self, a): return dic[a] if __name__ == "__main__": - assert Solution().findRepeatedDnaSequences("AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT") == ['AAAAACCCCC', 'CCCCCAAAAA'] \ No newline at end of file + assert Solution().findRepeatedDnaSequences("AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT") == ['CCCCCAAAAA', 'AAAAACCCCC'] diff --git a/518 Coin Change 2.py b/518 Coin Change 2.py index 9051483..b50a457 100644 --- a/518 Coin Change 2.py +++ b/518 Coin Change 2.py @@ -46,6 +46,7 @@ def change(self, amount, coins): n = len(coins) for l in range(n + 1): F[0][l] = 1 # trivial case + # why not start from 0, because we need to handle trivial case F[0][0] for a in range(1, amount + 1): for l in range(1, n + 1): From 8ce70f0c82ce73855d21885b8b438cba4ff26ec6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 17 Jun 2019 23:09:58 -0700 Subject: [PATCH 313/344] 829 --- 829 Consecutive Numbers Sum.py | 88 ++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 829 Consecutive Numbers Sum.py diff --git a/829 Consecutive Numbers Sum.py b/829 Consecutive Numbers Sum.py new file mode 100644 index 0000000..dc015b5 --- /dev/null +++ b/829 Consecutive Numbers Sum.py @@ -0,0 +1,88 @@ +#!/usr/bin/python3 +""" +Given a positive integer N, how many ways can we write it as a sum of consecutive +positive integers? + +Example 1: + +Input: 5 +Output: 2 +Explanation: 5 = 5 = 2 + 3 +Example 2: + +Input: 9 +Output: 3 +Explanation: 9 = 9 = 4 + 5 = 2 + 3 + 4 +Example 3: + +Input: 15 +Output: 4 +Explanation: 15 = 15 = 8 + 7 = 4 + 5 + 6 = 1 + 2 + 3 + 4 + 5 +Note: 1 <= N <= 10 ^ 9. +""" + + +class Solution: + def consecutiveNumbersSum(self, N: int) -> int: + """ + Arithmetic Array + math + + (x0 + xn) * (xn - x0 + 1) / 2 = N + xn = x0 + k - 1 + (2x0 + k - 1) * k / 2 = N + 2x0 = 2N / k - k + 1 + + x0 * k = N - k * (k - 1) / 2 + # assure for divisibility + """ + cnt = 0 + k = 0 + while True: + k += 1 + x0k = N - k * (k - 1) // 2 + if x0k <= 0 : + break + if x0k % k == 0: + cnt += 1 + + return cnt + + def consecutiveNumbersSum_error(self, N: int) -> int: + """ + Arithmetic Array + math + + (x0 + xn) * (xn - x0 + 1) / 2 = N + xn = x0 + k - 1 + (2x0 + k - 1) * k / 2 = N + 2x0 = 2N / k - k + 1 + + x0 * k = N - k * (k - 1) / 2 + # assure for divisibility + """ + cnt = 0 + for k in range(1, int(N ** 0.5)): # error + x0k = N - k * (k - 1) // 2 + if x0k % k == 0: + cnt += 1 + + return cnt + + def consecutiveNumbersSum_error(self, N: int) -> int: + """ + factor related + 9 / 3 = 3 + """ + if N == 1: + return 1 + + cnt = 0 + for i in range(1, N): + d = N // i + r = N % i + if r == 0 and d - i // 2 > 0: + cnt += 1 + elif r == 1 and N == (d + d + 1) * i // 2: + cnt += 1 + return cnt From 49ee4fdede1456010e69dfbc6026e4290b10e38c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 17 Jun 2019 23:18:10 -0700 Subject: [PATCH 314/344] 657 --- 657 Robot Return to Origin.py | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 657 Robot Return to Origin.py diff --git a/657 Robot Return to Origin.py b/657 Robot Return to Origin.py new file mode 100644 index 0000000..eec7d60 --- /dev/null +++ b/657 Robot Return to Origin.py @@ -0,0 +1,39 @@ +#!/usr/bin/python3 +""" +There is a robot starting at position (0, 0), the origin, on a 2D plane. Given +a sequence of its moves, judge if this robot ends up at (0, 0) after it +completes its moves. + +The move sequence is represented by a string, and the character moves[i] +represents its ith move. Valid moves are R (right), L (left), U (up), and D +(down). If the robot returns to the origin after it finishes all of its moves, +return true. Otherwise, return false. + +Note: The way that the robot is "facing" is irrelevant. "R" will always make the +robot move to the right once, "L" will always make it move left, etc. Also, +assume that the magnitude of the robot's movement is the same for each move. + +Example 1: + +Input: "UD" +Output: true +Explanation: The robot moves up once, and then down once. All moves have the +same magnitude, so it ended up at the origin where it started. Therefore, we +return true. + + +Example 2: + +Input: "LL" +Output: false +Explanation: The robot moves left twice. It ends up two "moves" to the left of +the origin. We return false because it is not at the origin at the end of its +moves. +""" +from collections import Counter + + +class Solution: + def judgeCircle(self, moves: str) -> bool: + counter = Counter(moves) + return counter["L"] == counter["R"] and counter["U"] == counter["D"] From 483768990de5c2b9c00ce9e70fdd964bee4a9e29 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 18 Jun 2019 23:43:44 -0700 Subject: [PATCH 315/344] 1041 Robot Bounded In Circle --- 1041 Robot Bounded In Circle.py | 72 +++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 1041 Robot Bounded In Circle.py diff --git a/1041 Robot Bounded In Circle.py b/1041 Robot Bounded In Circle.py new file mode 100644 index 0000000..7961da3 --- /dev/null +++ b/1041 Robot Bounded In Circle.py @@ -0,0 +1,72 @@ +#!/usr/bin/python3 +""" +On an infinite plane, a robot initially stands at (0, 0) and faces north. The robot can receive one of three instructions: + +"G": go straight 1 unit; +"L": turn 90 degrees to the left; +"R": turn 90 degress to the right. +The robot performs the instructions given in order, and repeats them forever. + +Return true if and only if there exists a circle in the plane such that the +robot never leaves the circle. + + + +Example 1: + +Input: "GGLLGG" +Output: true +Explanation: +The robot moves from (0,0) to (0,2), turns 180 degrees, and then returns to (0,0). +When repeating these instructions, the robot remains in the circle of radius 2 +centered at the origin. +Example 2: + +Input: "GG" +Output: false +Explanation: +The robot moves north indefinitely. +Example 3: + +Input: "GL" +Output: true +Explanation: +The robot moves from (0, 0) -> (0, 1) -> (-1, 1) -> (-1, 0) -> (0, 0) -> ... + + +Note: + +1 <= instructions.length <= 100 +instructions[i] is in {'G', 'L', 'R'} +""" + +dirs = [(-1, 0), (0, 1), (1, 0), (0, -1)] + + +class Solution: + def isRobotBounded(self, instructions: str) -> bool: + """ + LL: op + LLL: R + + L, R 90 degree + (GL) 90 needs 4 cycles to return back + 180 needs 2 cycles + 270 needs 4 cycles + + After 4 cycles, check whether the robot is at (0, 0) + """ + x, y = 0, 0 + i = 0 + for _ in range(4): + for cmd in instructions: + if cmd == "G": + dx, dy = dirs[i] + x += dx + y += dy + elif cmd == "L": + i = (i - 1) % 4 + else: + i = (i + 1) % 4 + + return x == 0 and y == 0 From af0b62b4382a66a704ec87ef28bbc1125dfa6e74 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 23 Jun 2019 23:50:00 -0700 Subject: [PATCH 316/344] 1000 Minimum Cost to Merge Stones --- 1000 Minimum Cost to Merge Stones.py | 118 +++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 1000 Minimum Cost to Merge Stones.py diff --git a/1000 Minimum Cost to Merge Stones.py b/1000 Minimum Cost to Merge Stones.py new file mode 100644 index 0000000..6c09420 --- /dev/null +++ b/1000 Minimum Cost to Merge Stones.py @@ -0,0 +1,118 @@ +#!/usr/bin/python3 +""" +There are N piles of stones arranged in a row. The i-th pile has stones[i] +stones. + +A move consists of merging exactly K consecutive piles into one pile, and the +cost of this move is equal to the total number of stones in these K piles. + +Find the minimum cost to merge all piles of stones into one pile. If it is +impossible, return -1. + + + +Example 1: + +Input: stones = [3,2,4,1], K = 2 +Output: 20 +Explanation: +We start with [3, 2, 4, 1]. +We merge [3, 2] for a cost of 5, and we are left with [5, 4, 1]. +We merge [4, 1] for a cost of 5, and we are left with [5, 5]. +We merge [5, 5] for a cost of 10, and we are left with [10]. +The total cost was 20, and this is the minimum possible. +Example 2: + +Input: stones = [3,2,4,1], K = 3 +Output: -1 +Explanation: After any merge operation, there are 2 piles left, and we can't +merge anymore. So the task is impossible. +Example 3: + +Input: stones = [3,5,1,2,6], K = 3 +Output: 25 +Explanation: +We start with [3, 5, 1, 2, 6]. +We merge [5, 1, 2] for a cost of 8, and we are left with [3, 8, 6]. +We merge [3, 8, 6] for a cost of 17, and we are left with [17]. +The total cost was 25, and this is the minimum possible. + + +Note: + +1 <= stones.length <= 30 +2 <= K <= 30 +1 <= stones[i] <= 100 +""" +from typing import List +from functools import lru_cache + + +class Solution: + def mergeStones(self, stones: List[int], K: int) -> int: + """ + Mergeable? K -> 1. Reduction size (K - 1) + N - (K - 1) * m = 1 + mergeable: (N - 1) % (K - 1) = 0 + + K consecutive + every piles involves at least once + Non-consecutive: priority queue merge the least first + But here it is consecutive, need to search, cannot gready + + * Merge the piles cost the same as merge individual ones. + + Attemp 1: + F[i] = cost to merge A[:i] into 1 + F[i] = F[i-3] + A[i-1] + A[i-2] + A[i-3] ?? + + Attemp 2: + F[i][j] = cost of merge A[i:j] into 1 + F[i][j] = F[i][k] + F[k][j] ?? + + Answer: + F[i][j][m] = cost of merge A[i:j] into m piles + F[i][j][1] = F[i][j][k] + sum(stones[i:j]) + F[i][j][m] = min F[i][mid][1] + F[mid][j][m-1] + + initial: + F[i][i+1][1] = 0 + F[i][i+1][m] = inf + + since the mid goes through the middle in the i, j. + Use memoization rather than dp + """ + N = len(stones) + sums = [0] + for s in stones: + sums.append(sums[-1] + s) + + @lru_cache(None) + def F(i, j, m): + if i >= j or m < 1: + return float("inf") + + n = j - i + if (n - m) % (K - 1) != 0: + return float("inf") + + if j == i + 1: + if m == 1: + return 0 + return float("inf") + + if m == 1: + return F(i, j, K) + sums[j] - sums[i] + + ret = min( + F(i, mid, 1) + F(mid, j, m - 1) + for mid in range(i + 1, j, K - 1) + ) + return ret + + ret = F(0, N, 1) + return ret if ret != float("inf") else -1 + + +if __name__ == "__main__": + assert Solution().mergeStones([3,5,1,2,6], 3) == 25 From 478cd85698f429b47a5b0bd485f2222d79c144a5 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 24 Jun 2019 21:56:45 -0700 Subject: [PATCH 317/344] 1058 Minimize Rounding Error to Meet Target --- 1000 Minimum Cost to Merge Stones.py | 6 +- ... Minimize Rounding Error to Meet Target.py | 68 +++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 1058 Minimize Rounding Error to Meet Target.py diff --git a/1000 Minimum Cost to Merge Stones.py b/1000 Minimum Cost to Merge Stones.py index 6c09420..88cf783 100644 --- a/1000 Minimum Cost to Merge Stones.py +++ b/1000 Minimum Cost to Merge Stones.py @@ -72,8 +72,8 @@ def mergeStones(self, stones: List[int], K: int) -> int: Answer: F[i][j][m] = cost of merge A[i:j] into m piles - F[i][j][1] = F[i][j][k] + sum(stones[i:j]) - F[i][j][m] = min F[i][mid][1] + F[mid][j][m-1] + F[i][j][1] = F[i][j][k] + sum(stones[i:j]) # merge + F[i][j][m] = min F[i][mid][1] + F[mid][j][m-1] # add initial: F[i][i+1][1] = 0 @@ -100,7 +100,7 @@ def F(i, j, m): if m == 1: return 0 return float("inf") - + if m == 1: return F(i, j, K) + sums[j] - sums[i] diff --git a/1058 Minimize Rounding Error to Meet Target.py b/1058 Minimize Rounding Error to Meet Target.py new file mode 100644 index 0000000..f735394 --- /dev/null +++ b/1058 Minimize Rounding Error to Meet Target.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 +""" +Given an array of prices [p1,p2...,pn] and a target, round each price pi to +Roundi(pi) so that the rounded array [Round1(p1),Round2(p2)...,Roundn(pn)] sums +to the given target. Each operation Roundi(pi) could be either Floor(pi) or +Ceil(pi). + +Return the string "-1" if the rounded array is impossible to sum to target. +Otherwise, return the smallest rounding error, which is defined as +Σ |Roundi(pi) - (pi)| for i from 1 to n, as a string with three places after the +decimal. + + + +Example 1: + +Input: prices = ["0.700","2.800","4.900"], target = 8 +Output: "1.000" +Explanation: +Use Floor, Ceil and Ceil operations to get (0.7 - 0) + (3 - 2.8) + (5 - 4.9) = +0.7 + 0.2 + 0.1 = 1.0 . +Example 2: + +Input: prices = ["1.500","2.500","3.500"], target = 10 +Output: "-1" +Explanation: +It is impossible to meet the target. + + +Note: + +1 <= prices.length <= 500. +Each string of prices prices[i] represents a real number which is between 0 and +1000 and has exactly 3 decimal places. +target is between 0 and 1000000. +""" +from typing import List +import math + + +class Solution: + def minimizeError(self, prices: List[str], target: int) -> str: + """ + to determine possible, floor all or ceil all + + floor all, sort by floor error inverse, make the adjustment + """ + A = list(map(float, prices)) + f_sum = sum(map(math.floor, A)) + c_sum = sum(map(math.ceil, A)) + if not f_sum <= target <= c_sum: + return "-1" + + errors = [ + e - math.floor(e) + for e in A + ] + errors.sort(reverse=True) + ret = 0 + remain = target - f_sum + for err in errors: + if remain > 0: + ret += 1 - err + remain -= 1 + else: + ret += err + + return f'{ret:.{3}f}' From f5ab752e127cc00cbadac32206e192ba27f2d2d7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 24 Jun 2019 22:38:32 -0700 Subject: [PATCH 318/344] 998 Maximum Binary Tree II --- 998 Maximum Binary Tree II.py | 67 +++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 998 Maximum Binary Tree II.py diff --git a/998 Maximum Binary Tree II.py b/998 Maximum Binary Tree II.py new file mode 100644 index 0000000..d7e03dc --- /dev/null +++ b/998 Maximum Binary Tree II.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +We are given the root node of a maximum tree: a tree where every node has a +value greater than any other value in its subtree. + +Just as in the previous problem, the given tree was constructed from an list A +(root = Construct(A)) recursively with the following Construct(A) routine: + +If A is empty, return null. +Otherwise, let A[i] be the largest element of A. Create a root node with value +A[i]. +The left child of root will be Construct([A[0], A[1], ..., A[i-1]]) +The right child of root will be Construct([A[i+1], A[i+2], ..., +A[A.length - 1]]) +Return root. +Note that we were not given A directly, only a root node root = Construct(A). + +Suppose B is a copy of A with the value val appended to it. It is guaranteed +that B has unique values. + +Return Construct(B). + +Example 1: +Input: root = [4,1,3,null,null,2], val = 5 +Output: [5,4,null,1,3,null,null,2] +Explanation: A = [1,4,2,3], B = [1,4,2,3,5] + +Example 2: +Input: root = [5,2,4,null,1], val = 3 +Output: [5,2,4,null,1,null,3] +Explanation: A = [2,1,5,4], B = [2,1,5,4,3] + +Example 3: +Input: root = [5,2,3,null,1], val = 4 +Output: [5,2,4,null,1,3] +Explanation: A = [2,1,5,3], B = [2,1,5,3,4] +""" + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def insertIntoMaxTree(self, root: TreeNode, val: int) -> TreeNode: + """ + Suppose B is a copy of A with the value val appended to it. + val is ALWAYS on the right + + insert the node in the root + Go through the example one by one. + Need to maintain the parent relationship -> return the sub-root + """ + if not root: + return TreeNode(val) + + if val > root.val: + node = TreeNode(val) + node.left = root + return node + + root.right = self.insertIntoMaxTree(root.right, val) + return root From 5c093924d28e18394b9fb1dfc4f3cf842d7e6112 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 21 Jul 2019 10:58:17 -0700 Subject: [PATCH 319/344] 041 Trapping Rain Water --- 041 Trapping Rain Water py3.py | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 041 Trapping Rain Water py3.py diff --git a/041 Trapping Rain Water py3.py b/041 Trapping Rain Water py3.py new file mode 100644 index 0000000..52cbe3e --- /dev/null +++ b/041 Trapping Rain Water py3.py @@ -0,0 +1,37 @@ +#!/usr/bin/python3 +""" +Given n non-negative integers representing an elevation map where the width of +each bar is 1, compute how much water it is able to trap after raining. + + +The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In +this case, 6 units of rain water (blue section) are being trapped. + +Example: +Input: [0,1,0,2,1,0,1,3,2,1,2,1] +Output: 6 +""" + +class Solution: + def trap(self, height: List[int]) -> int: + """ + At each position, the water is determined by the left and right max + + Let lefts[i] be the max(height[:i]) + Let rights[i] be the max(height[i:]) + """ + n = len(height) + lefts = [0 for _ in range(n+1)] + rights = [0 for _ in range(n+1)] + for i in range(1, n+1): # i, index of lefts + lefts[i] = max(lefts[i-1], height[i-1]) + for i in range(n-1, -1, -1): + rights[i] = max(rights[i+1], height[i]) + + ret = 0 + for i in range(n): + ret += max( + 0, + min(lefts[i], rights[i+1]) - height[i] + ) + return ret From 53abbd232b133a7f4ca6602b57a11be45af79a56 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 21 Jul 2019 15:26:37 -0700 Subject: [PATCH 320/344] Combination Sum, Combination Sum II --- 336 Palindrome Pairs.py | 8 ++++---- 759 Employee Free Time.py | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/336 Palindrome Pairs.py b/336 Palindrome Pairs.py index 76106b8..be86857 100644 --- a/336 Palindrome Pairs.py +++ b/336 Palindrome Pairs.py @@ -55,20 +55,20 @@ def palindromePairs(self, words: List[str]) -> List[List[int]]: for idx, w in enumerate(words): cur = root for i in range(len(w) - 1, -1, -1): - # cur.children[w[i]] # error, pre-forward unable to handle empty str - if self.is_palindrome(w, 0, i + 1): # exclude w[i] rather than include + # cur.children[w[i]] # error, pre-advancing the trie is unable to handle empty str + if self.is_palindrome(w, 0, i + 1): cur.pali_prefix_idxes.append(idx) cur = cur.children[w[i]] - cur.pali_prefix_idxes.append(idx) + cur.pali_prefix_idxes.append(idx) # empty str is palindrome cur.word_idx = idx # word ends ret = [] for idx, w in enumerate(words): cur = root for i in range(len(w)): - # cur.children.get(w[i], None) # error, pre-forward unable to handle empty str + # cur.children.get(w[i], None) # error, pre-advancing the trie is unable to handle empty str if self.is_palindrome(w, i, len(w)) and cur.word_idx is not None and cur.word_idx != idx: ret.append([idx, cur.word_idx]) diff --git a/759 Employee Free Time.py b/759 Employee Free Time.py index 463fbab..8866a7c 100644 --- a/759 Employee Free Time.py +++ b/759 Employee Free Time.py @@ -60,7 +60,7 @@ def employeeFreeTime(self, schedule: List[List[List[int]]]) -> List[List[int]]: Similar to meeting rooms II """ - max_end = min( + cur_max_end = min( itv[E] for itvs in schedule for itv in itvs @@ -76,10 +76,10 @@ def employeeFreeTime(self, schedule: List[List[List[int]]]) -> List[List[int]]: while q: _, i, j = heapq.heappop(q) itv = schedule[i][j] - if max_end < itv[S]: - ret.append([max_end, itv[S]]) + if cur_max_end < itv[S]: + ret.append([cur_max_end, itv[S]]) - max_end = max(max_end, itv[E]) + cur_max_end = max(cur_max_end, itv[E]) # next j += 1 @@ -122,7 +122,7 @@ def employeeFreeTime_error(self, schedule: List[List[List[int]]]) -> List[List[i use index instead """ schedules = list(map(iter, schedule)) - max_end = min( + cur_max_end = min( itv[E] for emp in schedule for itv in emp @@ -136,9 +136,9 @@ def employeeFreeTime_error(self, schedule: List[List[List[int]]]) -> List[List[i ret = [] while q: _, itv, emp_iter = heapq.heappop(q) - if max_end < itv[S]: - ret.append([max_end, itv[S]]) - max_end = max(max_end, itv[E]) + if cur_max_end < itv[S]: + ret.append([cur_max_end, itv[S]]) + cur_max_end = max(cur_max_end, itv[E]) itv = next(emp_iter, None) if itv: heapq.heappush(q, (itv[S], itv, emp_iter)) From e0cc5c8fc757c023cd889f1ab61c24e3093bd84d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 21 Jul 2019 15:26:56 -0700 Subject: [PATCH 321/344] Combination Sum, Combination Sum II --- 038 Combination Sum II py3.py | 67 +++++++++++++++++++++++++++++++++++ 039 Combination Sum py3.py | 57 +++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 038 Combination Sum II py3.py create mode 100644 039 Combination Sum py3.py diff --git a/038 Combination Sum II py3.py b/038 Combination Sum II py3.py new file mode 100644 index 0000000..7658b27 --- /dev/null +++ b/038 Combination Sum II py3.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +Given a collection of candidate numbers (candidates) and a target number +(target), find all unique combinations in candidates where the candidate numbers +sums to target. + +Each number in candidates may only be used once in the combination. + +Note: + +All numbers (including target) will be positive integers. +The solution set must not contain duplicate combinations. +Example 1: + +Input: candidates = [10,1,2,7,6,1,5], target = 8, +A solution set is: +[ + [1, 7], + [1, 2, 5], + [2, 6], + [1, 1, 6] +] +Example 2: + +Input: candidates = [2,5,2,1,2], target = 5, +A solution set is: +[ + [1,2,2], + [5] +] +""" +from typing import List + + +class Solution: + def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: + ret = [] + candidates.sort() + self.dfs(candidates, 0, [], 0, target, ret) + return ret + + def dfs(self, candidates, i, cur, cur_sum, target, ret): + if cur_sum == target: + ret.append(list(cur)) + return + + if cur_sum > target or i >= len(candidates): + return + + # not choose A_i + # to de-dup, need to jump + j = i + 1 + while j < len(candidates) and candidates[j] == candidates[i]: + j += 1 + + self.dfs(candidates, j, cur, cur_sum, target, ret) + + # choose A_i + cur.append(candidates[i]) + cur_sum += candidates[i] + self.dfs(candidates, i + 1, cur, cur_sum, target, ret) + cur.pop() + cur_sum -= candidates[i] + + +if __name__ == "__main__": + assert Solution().combinationSum2([2,5,2,1,2], 5) == [[5], [1,2,2]] diff --git a/039 Combination Sum py3.py b/039 Combination Sum py3.py new file mode 100644 index 0000000..3f758dd --- /dev/null +++ b/039 Combination Sum py3.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +""" +Given a set of candidate numbers (candidates) (without duplicates) and a target +number (target), find all unique combinations in candidates where the candidate +numbers sums to target. + +The same repeated number may be chosen from candidates unlimited number of +times. + +Note: + +All numbers (including target) will be positive integers. +The solution set must not contain duplicate combinations. +Example 1: + +Input: candidates = [2,3,6,7], target = 7, +A solution set is: +[ + [7], + [2,2,3] +] +Example 2: + +Input: candidates = [2,3,5], target = 8, +A solution set is: +[ + [2,2,2,2], + [2,3,3], + [3,5] +] +""" +from typing import List + + +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + ret = [] + self.dfs(candidates, 0, [], 0, target, ret) + return ret + + def dfs(self, candidates, i, cur, cur_sum, target, ret): + if cur_sum == target: + ret.append(list(cur)) + return + + if cur_sum > target or i >= len(candidates): + return + + # not choose A_i + self.dfs(candidates, i + 1, cur, cur_sum, target, ret) + + # choose A_i + cur.append(candidates[i]) + cur_sum += candidates[i] + self.dfs(candidates, i, cur, cur_sum, target, ret) + cur.pop() + cur_sum -= candidates[i] From 26956a7da12b2a33059b39e98286dcc5a839c9c6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 21 Jul 2019 16:05:05 -0700 Subject: [PATCH 322/344] Bulls and Cows --- 216 Combination Sum III.py | 9 +++++---- 299 Bulls and Cows.py | 14 +++++++++----- 377 Combination Sum IV.py | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/216 Combination Sum III.py b/216 Combination Sum III.py index e4601b3..0547efa 100644 --- a/216 Combination Sum III.py +++ b/216 Combination Sum III.py @@ -42,17 +42,18 @@ def dfs(self, remain_k, remain_n, cur, ret): ret.append(list(cur)) return - if remain_k*9 < remain_n or remain_k*1 > remain_n: + # check max and min reach + if remain_k * 9 < remain_n or remain_k * 1 > remain_n: return start = 1 if cur: - start = cur[-1]+1 # unique + start = cur[-1] + 1 # unique for i in xrange(start, 10): cur.append(i) - self.dfs(remain_k-1, remain_n-i, cur, ret) + self.dfs(remain_k - 1, remain_n - i, cur, ret) cur.pop() if __name__ == "__main__": - assert Solution().combinationSum3(3, 9) == [[1, 2, 6], [1, 3, 5], [2, 3, 4]] \ No newline at end of file + assert Solution().combinationSum3(3, 9) == [[1, 2, 6], [1, 3, 5], [2, 3, 4]] diff --git a/299 Bulls and Cows.py b/299 Bulls and Cows.py index b9fe21d..26a05f5 100644 --- a/299 Bulls and Cows.py +++ b/299 Bulls and Cows.py @@ -6,6 +6,12 @@ class Solution(object): def getHint(self, secret, guess): """ + Need to revert B + + Example: + 1121 + 2323 + :type secret: str :type guess: str :rtype: str @@ -19,12 +25,10 @@ def getHint(self, secret, guess): for i, v in enumerate(guess): if v == secret[i]: A += 1 - cnt[v] -= 1 - if cnt[v] < 0: - # revert matched B - assert cnt[v] == -1 + if cnt[v] > 0: + cnt[v] -= 1 + else: B -= 1 - cnt[v] = 0 elif cnt[v] > 0: B += 1 diff --git a/377 Combination Sum IV.py b/377 Combination Sum IV.py index 19533ae..1d3d20a 100644 --- a/377 Combination Sum IV.py +++ b/377 Combination Sum IV.py @@ -53,4 +53,4 @@ def combinationSum4(self, nums, target): if __name__ == "__main__": - assert Solution().combinationSum4([1, 2, 3], 4) == 7 \ No newline at end of file + assert Solution().combinationSum4([1, 2, 3], 4) == 7 From 0f143c1b6262f8c491679578c9b498bcba38ec7b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 22 Jul 2019 09:07:42 -0700 Subject: [PATCH 323/344] Word Search II --- 212 Word Search II py3.py | 71 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 212 Word Search II py3.py diff --git a/212 Word Search II py3.py b/212 Word Search II py3.py new file mode 100644 index 0000000..705d28a --- /dev/null +++ b/212 Word Search II py3.py @@ -0,0 +1,71 @@ +#!/usr/bin/python3 +""" +Given a 2D board and a list of words from the dictionary, find all words in the board. + +Each word must be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally +or vertically neighboring. The same letter cell may not be used more than once in a word. + +For example, +Given words = ["oath","pea","eat","rain"] and board = + +[ + ['o','a','a','n'], + ['e','t','a','e'], + ['i','h','k','r'], + ['i','f','l','v'] +] +Return ["eat","oath"]. +Note: +You may assume that all inputs are consist of lowercase letters a-z. +""" +from typing import List +from collections import defaultdict + + +dirs = [(0, 1), (0, -1), (-1, 0), (1, 0)] + + +class TrieNode: + def __init__(self): + self.word = None + self.children = defaultdict(TrieNode) + + +class Solution: + def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: + root = self.construct(words) + m, n = len(board), len(board[0]) + visited = [[False for _ in range(n)] for _ in range(m)] + ret = set() + for i in range(m): + for j in range(n): + self.dfs(board, visited, i, j, root, ret) + + return list(ret) + + def dfs(self, board, visited, i, j, cur, ret): + m, n = len(board), len(board[0]) + visited[i][j] = True + c = board[i][j] + if c in cur.children: + nxt = cur.children[c] + if nxt.word is not None: + ret.add(nxt.word) + + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < m and 0 <= J < n and not visited[I][J]: + self.dfs(board, visited, I, J, nxt, ret) + + visited[i][j] = False + + def construct(self, words): + root = TrieNode() + for w in words: + cur = root + for c in w: + cur = cur.children[c] + cur.word = w + + return root From 6eec4d13f2d0f65270a99cc316d762e3ded37e02 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 6 Aug 2019 21:38:52 -0800 Subject: [PATCH 324/344] 324 Wiggle Sort II --- 068 Text Justification py3.py | 34 +++++++++++++ 269 Alien Dictionary py3.py | 2 +- 280 Wiggle Sort.py | 4 +- 324 Wiggle Sort II py3.py | 79 +++++++++++++++++++++++++++++ 324 Wiggle Sort II.py | 7 ++- 341 Flatten Nested List Iterator.py | 17 +++---- 751 IP to CIDR.py | 10 ++++ 773 Sliding Puzzle.py | 2 + 8 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 324 Wiggle Sort II py3.py diff --git a/068 Text Justification py3.py b/068 Text Justification py3.py index 0eac360..ba48272 100644 --- a/068 Text Justification py3.py +++ b/068 Text Justification py3.py @@ -61,6 +61,40 @@ class Solution: def fullJustify(self, words: List[str], maxWidth: int) -> List[str]: """ Round robin distribution of spaces + + Jump and then backtrack + """ + ret = [] + char_cnt = 0 # char exclude spaces + cur_words = [] + + for w in words: + cur_words.append(w) + char_cnt += len(w) + if char_cnt + len(cur_words) - 1 > maxWidth: + # break + cur_words.pop() + char_cnt -= len(w) + for i in range(maxWidth - char_cnt): + cur_words[i % max(1, len(cur_words) - 1)] += " " + + ret.append("".join(cur_words)) + + cur_words = [w] + char_cnt = len(w) + + # last line + last = " ".join(cur_words) + ret.append(last + " " * (maxWidth - len(last))) + return ret + + +class Solution2: + def fullJustify(self, words: List[str], maxWidth: int) -> List[str]: + """ + Round robin distribution of spaces + + Look before jump """ ret = [] char_cnt = 0 diff --git a/269 Alien Dictionary py3.py b/269 Alien Dictionary py3.py index 10a0c41..3d35408 100644 --- a/269 Alien Dictionary py3.py +++ b/269 Alien Dictionary py3.py @@ -63,7 +63,7 @@ def construct_graph(self, words): for i in range(len(words) - 1): # compare word_i and word_{i+1} for c1, c2 in zip(words[i], words[i+1]): - if c1 != c2: + if c1 != c2: # lexical order G[c1].append(c2) break # need to break for lexical order diff --git a/280 Wiggle Sort.py b/280 Wiggle Sort.py index 7c3d30e..5752e51 100644 --- a/280 Wiggle Sort.py +++ b/280 Wiggle Sort.py @@ -9,6 +9,8 @@ class Solution(object): def wiggleSort(self, nums): """ Solve by enumerating examples + Sort-based: interleave the small half and large half + Time: O(n lg n) Space: O(1) :type nums: List[int] @@ -19,4 +21,4 @@ def wiggleSort(self, nums): if i >= len(nums): i = 1 nums[i] = elt - i += 2 \ No newline at end of file + i += 2 diff --git a/324 Wiggle Sort II py3.py b/324 Wiggle Sort II py3.py new file mode 100644 index 0000000..f7c4798 --- /dev/null +++ b/324 Wiggle Sort II py3.py @@ -0,0 +1,79 @@ +#!/usr/bin/python3 +""" +Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] +< nums[3].... + +Example 1: + +Input: nums = [1, 5, 1, 1, 6, 4] +Output: One possible answer is [1, 4, 1, 5, 1, 6]. +Example 2: + +Input: nums = [1, 3, 2, 2, 3, 1] +Output: One possible answer is [2, 3, 1, 3, 1, 2]. +Note: +You may assume all input has valid answer. + +Follow Up: +Can you do it in O(n) time and/or in-place with O(1) extra space? +""" +from typing import List + + +class Solution: + def wiggleSort(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + + Median + 3-way partitioning + """ + n = len(nums) + # mid = self.find_kth(nums, 0, n, (n - 1) // 2) + # median = nums[mid] + median = list(sorted(nums))[n//2] + + # three way pivot + odd = 1 + even = n - 1 if (n - 1) % 2 == 0 else n - 2 + i = 0 + while i < n: + if nums[i] < median: + if i >= even and i % 2 == 0: + i += 1 + continue + nums[i], nums[even] = nums[even], nums[i] + even -= 2 + + elif nums[i] > median: + if i <= odd and i % 2 == 1: + i += 1 + continue + nums[i], nums[odd] = nums[odd], nums[i] + odd += 2 + else: + i += 1 + + def find_kth(self, A, lo, hi, k): + p = self.pivot(A, lo, hi) + if k == p: + return p + elif k > p: + return self.find_kth(A, p + 1, hi, k) + else: + return self.find_kth(A, lo, p, k) + + def pivot(self, A, lo, hi): + # need 3-way pivot, otherwise TLE + p = lo + closed = lo + for i in range(lo + 1, hi): + if A[i] < A[p]: + closed += 1 + A[closed], A[i] = A[i], A[closed] + + A[closed], A[p] = A[p], A[closed] + return closed + + +if __name__ == "__main__": + Solution().wiggleSort([1, 5, 1, 1, 6, 4]) diff --git a/324 Wiggle Sort II.py b/324 Wiggle Sort II.py index 6dead5c..bb2bf19 100644 --- a/324 Wiggle Sort II.py +++ b/324 Wiggle Sort II.py @@ -29,7 +29,7 @@ def wiggleSort(self, A): median_idx = self.find_kth(A, 0, n, n/2) v = A[median_idx] - idx = lambda i: (2*i+1)%(n|1) + idx = lambda i: (2*i+1) % (n|1) lt = -1 hi = n i = 0 @@ -92,9 +92,9 @@ def wiggleSort(self, nums): n = len(nums) A = sorted(nums) - j, k = (n-1)/2, n-1 + j, k = (n-1) / 2, n-1 for i in xrange(len(nums)): - if i%2 == 0: + if i % 2 == 0: nums[i] = A[j] j -= 1 else: @@ -107,4 +107,3 @@ def wiggleSort(self, nums): A = [3, 2, 1, 1, 3, 2] Solution().wiggleSort(A) print A - diff --git a/341 Flatten Nested List Iterator.py b/341 Flatten Nested List Iterator.py index 3866387..3a71429 100644 --- a/341 Flatten Nested List Iterator.py +++ b/341 Flatten Nested List Iterator.py @@ -56,21 +56,20 @@ def __init__(self, nestedList): Linear structure usually use stack as structure. Iterator Invariant: 1. has the value to be returned ready: idx pointing to the integer to be return in the next(). - 2. move the pointer in hasNext() + 2. rember move the parent pointer in hasNext() Possible to compile nl and idx into a tuple. """ - self.stk = [[nestedList, 0]] + self.stk = [[nestedList, 0]] # stack of iterators def next(self): """ :rtype: int """ - if self.hasNext(): - nl, idx = self.stk[-1] - nxt = nl[idx].getInteger() - self.stk[-1][1] = idx + 1 - return nxt + nl, idx = self.stk[-1] + nxt = nl[idx].getInteger() + self.stk[-1][1] = idx + 1 # advance the index + return nxt def hasNext(self): """ @@ -84,7 +83,7 @@ def hasNext(self): if ni.isInteger(): return True else: - self.stk[-1][1] = idx + 1 + self.stk[-1][1] = idx + 1 # prepare the parent, otherwise dead loop nxt_nl = ni.getList() self.stk.append([nxt_nl, 0]) else: @@ -149,4 +148,4 @@ def hasNext(self): # Your NestedIterator object will be instantiated and called as such: # i, v = NestedIterator(nestedList), [] -# while i.hasNext(): v.append(i.next()) \ No newline at end of file +# while i.hasNext(): v.append(i.next()) diff --git a/751 IP to CIDR.py b/751 IP to CIDR.py index cc69b9c..89e9e91 100644 --- a/751 IP to CIDR.py +++ b/751 IP to CIDR.py @@ -69,6 +69,16 @@ class Solution: def ipToCIDR(self, ip: str, n: int) -> List[str]: """ bit manipulation + + IP address, 8 bit, at each digit, total 32 bit, 8 byte + Follow the example + Input: ip = "255.0.0.7", n = 10 + Output: ["255.0.0.7/32","255.0.0.8/29","255.0.0.16/32"] + 255.0.0.7 -> 11111111 00000000 00000000 00000111 => cover 1 + 255.0.0.8 -> 11111111 00000000 00000000 00001000 => cover 8 + 255.0.0.16 -> 11111111 00000000 00000000 00010000 => cover 16 but only 32 + 32 means all bits as fixed prefix + 111, then 32 to cover only one, depends on LSB Greedy To cover n, can have representation covers > n diff --git a/773 Sliding Puzzle.py b/773 Sliding Puzzle.py index 637e766..6ec2fda 100644 --- a/773 Sliding Puzzle.py +++ b/773 Sliding Puzzle.py @@ -71,6 +71,8 @@ def slidingPuzzle(self, board: List[List[int]]) -> int: Chain the matrix into 1d array. N = R * C Complexity O(N * N!) There are O(N!) possible board states. O(N) is the time to scan the board for the operations in the loop. + + Possible to reduce the 2D array in a 1D array and do %C and //C, where C is the size of the column """ visited = defaultdict(bool) m, n = len(board), len(board[0]) From 468b7591f502a0ff704281292b34193a13d7b1c3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 20 Aug 2019 22:05:12 -0700 Subject: [PATCH 325/344] Sentence Screen Fitting --- 058 Spiral Matrix II.py | 5 ++- 418 Sentence Screen Fitting.py | 77 ++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 418 Sentence Screen Fitting.py diff --git a/058 Spiral Matrix II.py b/058 Spiral Matrix II.py index 003de91..95e7359 100644 --- a/058 Spiral Matrix II.py +++ b/058 Spiral Matrix II.py @@ -12,6 +12,8 @@ ] """ __author__ = 'Danyang' + + class Solution: def generateMatrix(self, n): """ @@ -48,7 +50,8 @@ def generateMatrix(self, n): return result + if __name__=="__main__": result = Solution().generateMatrix(4) for row in result: - print row \ No newline at end of file + print row diff --git a/418 Sentence Screen Fitting.py b/418 Sentence Screen Fitting.py new file mode 100644 index 0000000..cddb779 --- /dev/null +++ b/418 Sentence Screen Fitting.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 +""" +Given a rows x cols screen and a sentence represented by a list of non-empty +words, find how many times the given sentence can be fitted on the screen. + +Note: +A word cannot be split into two lines. +The order of words in the sentence must remain unchanged. +Two consecutive words in a line must be separated by a single space. +Total words in the sentence won't exceed 100. +Length of each word is greater than 0 and won't exceed 10. +1 ≤ rows, cols ≤ 20,000. +Example 1: + +Input: +rows = 2, cols = 8, sentence = ["hello", "world"] + +Output: +1 + +Explanation: +hello--- +world--- + +The character '-' signifies an empty space on the screen. +Example 2: + +Input: +rows = 3, cols = 6, sentence = ["a", "bcd", "e"] + +Output: +2 + +Explanation: +a-bcd- +e-a--- +bcd-e- + +The character '-' signifies an empty space on the screen. +Example 3: + +Input: +rows = 4, cols = 5, sentence = ["I", "had", "apple", "pie"] + +Output: +1 + +Explanation: +I-had +apple +pie-I +had-- + +The character '-' signifies an empty space on the screen. +""" +from typing import List + + +class Solution: + def wordsTyping(self, sentence: List[str], rows: int, cols: int) -> int: + """ + How many times to fit + + Combine the words in to a string and wrap it around + """ + sentence = " ".join(sentence) + " " # unify the condition checking for the last word; tail will wrap with head with space + i = 0 + for r in range(rows): + i += cols + while sentence[i % len(sentence)] != " ": + i -= 1 + + # now sentence[i] is " " + i += 1 + + ret = i // len(sentence) + return ret From b5c7f15adcb425e3415ea2dcedd23da8d803f0f5 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 7 Sep 2019 23:20:01 -0700 Subject: [PATCH 326/344] 480 Sliding Window Median --- 480 Sliding Window Median.py | 135 +++++++++++++++++++++++++++++++++++ 772 Basic Calculator III.py | 3 +- 2 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 480 Sliding Window Median.py diff --git a/480 Sliding Window Median.py b/480 Sliding Window Median.py new file mode 100644 index 0000000..1a05d0e --- /dev/null +++ b/480 Sliding Window Median.py @@ -0,0 +1,135 @@ +#!/usr/bin/python3 +""" +Median is the middle value in an ordered integer list. If the size of the list +is even, there is no middle value. So the median is the mean of the two middle +value. + +Examples: +[2,3,4] , the median is 3 + +[2,3], the median is (2 + 3) / 2 = 2.5 + +Given an array nums, there is a sliding window of size k which is moving from +the very left of the array to the very right. You can only see the k numbers in +the window. Each time the sliding window moves right by one position. Your job +is to output the median array for each window in the original array. + +For example, +Given nums = [1,3,-1,-3,5,3,6,7], and k = 3. + +Window position Median +--------------- ----- +[1 3 -1] -3 5 3 6 7 1 + 1 [3 -1 -3] 5 3 6 7 -1 + 1 3 [-1 -3 5] 3 6 7 -1 + 1 3 -1 [-3 5 3] 6 7 3 + 1 3 -1 -3 [5 3 6] 7 5 + 1 3 -1 -3 5 [3 6 7] 6 +Therefore, return the median sliding window as [1,-1,-1,3,5,6]. + +Note: +You may assume k is always valid, ie: k is always smaller than input array's +size for non-empty array. +""" +from typing import List +import heapq + + +class DualHeap: + def __init__(self): + """ + ---- number line ---> + --- max heap --- | --- min heap --- + """ + self.max_h = [] # List[Tuple[comparator, num]] + self.min_h = [] + self.max_sz = 0 + self.min_sz = 0 + self.to_remove = set() # value, error mapping index in nums + + def insert(self, num): + if self.max_h and num > self.max_h[0][1]: + heapq.heappush(self.min_h, (num, num)) + self.min_sz += 1 + else: + heapq.heappush(self.max_h, (-num, num)) + self.max_sz += 1 + self.balance() + + def pop(self, num): + self.to_remove.add(num) + if self.max_h and num > self.max_h[0][1]: + self.min_sz -= 1 + else: + self.max_sz -= 1 + self.balance() + + def clean_top(self): + while self.max_h and self.max_h[0][1] in self.to_remove: + _, num = heapq.heappop(self.max_h) + self.to_remove.remove(num) + while self.min_h and self.min_h[0][1] in self.to_remove: + _, num = heapq.heappop(self.min_h) + self.to_remove.remove(num) + + def balance(self): + # keep skew in max sz + while self.max_sz < self.min_sz : + self.clean_top() + _, num =heapq.heappop(self.min_h) + heapq.heappush(self.max_h, (-num, num)) + self.min_sz -= 1 + self.max_sz += 1 + while self.max_sz > self.min_sz + 1: + self.clean_top() + _, num = heapq.heappop(self.max_h) + heapq.heappush(self.min_h, (num, num)) + self.min_sz += 1 + self.max_sz -= 1 + + self.clean_top() + + def get_median(self, k): + self.clean_top() + if k % 2 == 1: + return self.max_h[0][1] + else: + return 0.5 * (self.max_h[0][1] + self.min_h[0][1]) + + +class Solution: + def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: + """ + 1. BST, proxied by bisect + dual heap + lazy removal + balance the valid element + + --- max heap --- | --- min heap --- + but need to delete the start of the window + + Lazy Removal with the help of hash table of idx -> remove? + Hash table mapping idx will fail + Remove by index will introduce bug for test case [1,1,1,1], 2: when poping, + we cannot know which heap to go to by index since decision of which heap to pop + is only about value. + + Calculating median also doesn't care about index, it only cares about value + """ + ret = [] + dh = DualHeap() + for i in range(k): + dh.insert(nums[i]) + + ret.append(dh.get_median(k)) + + for i in range(k, len(nums)): + dh.insert(nums[i]) + dh.pop(nums[i-k]) + ret.append(dh.get_median(k)) + + return ret + + +if __name__ == "__main__": + assert Solution().medianSlidingWindow([-2147483648,-2147483648,2147483647,-2147483648,-2147483648,-2147483648,2147483647,2147483647,2147483647,2147483647,-2147483648,2147483647,-2147483648], 2) + assert Solution().medianSlidingWindow([1,1,1,1], 2) == [1, 1, 1] + assert Solution().medianSlidingWindow([1,3,-1,-3,5,3,6,7], 3) == [1,-1,-1,3,5,6] diff --git a/772 Basic Calculator III.py b/772 Basic Calculator III.py index f5f4754..4e659c9 100644 --- a/772 Basic Calculator III.py +++ b/772 Basic Calculator III.py @@ -25,6 +25,7 @@ class Solution: def calculate(self, s: str) -> int: """ + make +, - lower precedence operator as a unary operation recursively handle bracket """ s = s + "\0" # signal the end @@ -69,7 +70,7 @@ def eval(self, s, i, stk): raise return sum(stk), i - + if __name__ == "__main__": assert Solution().calculate("(2+6* 3+5- (3*14/7+2)*5)+3") == -12 From 9ac3da6897eb96eff1ddc7b62b349aa22b872db8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 12 Sep 2019 17:02:36 -0700 Subject: [PATCH 327/344] 1114 Print in Order --- 1114 Print in Order.py | 98 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 1114 Print in Order.py diff --git a/1114 Print in Order.py b/1114 Print in Order.py new file mode 100644 index 0000000..e64757f --- /dev/null +++ b/1114 Print in Order.py @@ -0,0 +1,98 @@ +#!/usr/bin/python3 +""" +Suppose we have a class: + +public class Foo { + public void first() { print("first"); } + public void second() { print("second"); } + public void third() { print("third"); } +} +The same instance of Foo will be passed to three different threads. Thread A +will call first(), thread B will call second(), and thread C will call third(). +Design a mechanism and modify the program to ensure that second() is executed +after first(), and third() is executed after second(). + + + +Example 1: + +Input: [1,2,3] +Output: "firstsecondthird" +Explanation: There are three threads being fired asynchronously. The input +[1,2,3] means thread A calls first(), thread B calls second(), and thread C +calls third(). "firstsecondthird" is the correct output. +Example 2: + +Input: [1,3,2] +Output: "firstsecondthird" +Explanation: The input [1,3,2] means thread A calls first(), thread B calls +third(), and thread C calls second(). "firstsecondthird" is the correct output. +""" +from typing import Callable +from threading import Lock + + +class Foo: + def __init__(self): + """ + Two locks + """ + self.locks = [Lock(), Lock()] + self.locks[0].acquire() + self.locks[1].acquire() + + + def first(self, printFirst: Callable[[], None]) -> None: + # printFirst() outputs "first". Do not change or remove this line. + printFirst() + self.locks[0].release() + + + + def second(self, printSecond: Callable[[], None]) -> None: + with self.locks[0]: + # printSecond() outputs "second". Do not change or remove this line. + printSecond() + self.locks[1].release() + + + def third(self, printThird: Callable[[], None]) -> None: + with self.locks[1]: + # printThird() outputs "third". Do not change or remove this line. + printThird() + + +class FooError: + def __init__(self): + """ + Have a counter, and only the corresponding method can change update the + counter. + + Error, will miss an input. + """ + self._value = 1 + self._lock = Lock() + + + def first(self, printFirst: 'Callable[[], None]') -> None: + with self._lock: + if self._value == 1: + # printFirst() outputs "first". Do not change or remove this line. + self._value += 1 + printFirst() + + + def second(self, printSecond: 'Callable[[], None]') -> None: + with self._lock: + if self._value == 2: + # printSecond() outputs "second". Do not change or remove this line. + self._value += 1 + printSecond() + + + def third(self, printThird: 'Callable[[], None]') -> None: + with self._lock: + if self._value == 3: + # printThird() outputs "third". Do not change or remove this line. + self._value += 1 + printThird() From 09930cf95ac5bb42a7308d4b8f9985aa4f473066 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 12 Sep 2019 17:14:30 -0700 Subject: [PATCH 328/344] 1115 Print FooBar Alternately --- 1115 Print FooBar Alternately.py | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 1115 Print FooBar Alternately.py diff --git a/1115 Print FooBar Alternately.py b/1115 Print FooBar Alternately.py new file mode 100644 index 0000000..50deac2 --- /dev/null +++ b/1115 Print FooBar Alternately.py @@ -0,0 +1,60 @@ +#!/usr/bin/python3 +""" +Suppose you are given the following code: + +class FooBar { + public void foo() { + for (int i = 0; i < n; i++) { + print("foo"); + } + } + + public void bar() { + for (int i = 0; i < n; i++) { + print("bar"); + } + } +} +The same instance of FooBar will be passed to two different threads. Thread A +will call foo() while thread B will call bar(). Modify the given program to +output "foobar" n times. + + + +Example 1: + +Input: n = 1 +Output: "foobar" +Explanation: There are two threads being fired asynchronously. One of them calls +foo(), while the other calls bar(). "foobar" is being output 1 time. +Example 2: + +Input: n = 2 +Output: "foobarfoobar" +Explanation: "foobar" is being output 2 times. +""" +from threading import Lock +from typing import Callable + + +class FooBar: + def __init__(self, n): + self.n = n + self.locks = [Lock(), Lock()] + self.locks[1].acquire() + + + def foo(self, printFoo: Callable[[], None]) -> None: + for i in range(self.n): + self.locks[0].acquire() + # printFoo() outputs "foo". Do not change or remove this line. + printFoo() + self.locks[1].release() + + + def bar(self, printBar: Callable[[], None]) -> None: + for i in range(self.n): + self.locks[1].acquire() + # printBar() outputs "bar". Do not change or remove this line. + printBar() + self.locks[0].release() From 9c39e2c4a220513fbaf632342745d2bb517cbc4e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 12 Sep 2019 18:12:55 -0700 Subject: [PATCH 329/344] 1116 Print Zero Even Odd --- 1116 Print Zero Even Odd.py | 94 +++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 1116 Print Zero Even Odd.py diff --git a/1116 Print Zero Even Odd.py b/1116 Print Zero Even Odd.py new file mode 100644 index 0000000..4079201 --- /dev/null +++ b/1116 Print Zero Even Odd.py @@ -0,0 +1,94 @@ +#!/usr/bin/python3 +""" +uppose you are given the following code: + +class ZeroEvenOdd { + public ZeroEvenOdd(int n) { ... } // constructor + public void zero(printNumber) { ... } // only output 0's + public void even(printNumber) { ... } // only output even numbers + public void odd(printNumber) { ... } // only output odd numbers +} +The same instance of ZeroEvenOdd will be passed to three different threads: + +Thread A will call zero() which should only output 0's. +Thread B will call even() which should only ouput even numbers. +Thread C will call odd() which should only output odd numbers. +Each of the threads is given a printNumber method to output an integer. Modify +the given program to output the series 010203040506... where the length of the +series must be 2n. + + +Example 1: + +Input: n = 2 +Output: "0102" +Explanation: There are three threads being fired asynchronously. One of them +calls zero(), the other calls even(), and the last one calls odd(). "0102" is +the correct output. +Example 2: + +Input: n = 5 +Output: "0102030405" +""" +from threading import Lock + + +class ZeroEvenOdd: + def __init__(self, n): + """ + only use 3 locks, and zero() knows and commonds which lock to release, + determing whether even() or odd() will run. + """ + self.n = n + self.locks = [Lock() for _ in range(3)] + self.locks[1].acquire() + self.locks[2].acquire() + + # printNumber(x) outputs "x", where x is an integer. + def zero(self, printNumber: 'Callable[[int], None]') -> None: + for i in range(self.n): + self.locks[0].acquire() + printNumber(0) + if (i + 1) % 2 == 1: + self.locks[1].release() + else: + self.locks[2].release() + + def odd(self, printNumber: 'Callable[[int], None]') -> None: + for i in range(self.n // 2): + self.locks[1].acquire() + printNumner(i * 2 + 1) + self.locks[0].release() + + def even(self, printNumber: 'Callable[[int], None]') -> None: + for i in range(self.n // 2): + self.locks[2].acquire() + printNumber(i * 2 + 2) + self.locks[0].release() + + +class ZeroEvenOddError: + def __init__(self, n): + """ + Like 1115, two layer of locks can do: zero and non-zero alternating, + odd and even alternating. 4 locks required. + + Using only 3 locks? + """ + self.n = n + self.locks = [Lock(), Lock(), Lock(), Lock()] + for i in range(1, len(self.locks)): + self.locks[i].acquire() + + # printNumber(x) outputs "x", where x is an integer. + def zero(self, printNumber: 'Callable[[int], None]') -> None: + with self.locks[0]: + printNumber(0) + + def even(self, printNumber: 'Callable[[int], None]') -> None: + # cannot lock self.locks[1] from both "even" and "odd" + pass + + + def odd(self, printNumber: 'Callable[[int], None]') -> None: + pass From 11b06b291a16f25690e92cb79de5771a0ffa7f68 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 12 Sep 2019 18:18:47 -0700 Subject: [PATCH 330/344] 1116 Print Zero Even Odd --- 1116 Print Zero Even Odd.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/1116 Print Zero Even Odd.py b/1116 Print Zero Even Odd.py index 4079201..209fad0 100644 --- a/1116 Print Zero Even Odd.py +++ b/1116 Print Zero Even Odd.py @@ -37,7 +37,7 @@ class ZeroEvenOdd: def __init__(self, n): """ only use 3 locks, and zero() knows and commonds which lock to release, - determing whether even() or odd() will run. + determing whether even() or odd() will run. """ self.n = n self.locks = [Lock() for _ in range(3)] @@ -55,9 +55,9 @@ def zero(self, printNumber: 'Callable[[int], None]') -> None: self.locks[2].release() def odd(self, printNumber: 'Callable[[int], None]') -> None: - for i in range(self.n // 2): + for i in range((self.n + 1) // 2): self.locks[1].acquire() - printNumner(i * 2 + 1) + printNumber(i * 2 + 1) self.locks[0].release() def even(self, printNumber: 'Callable[[int], None]') -> None: From aa6c66418195e5bf6c30d02f15fd8b7acbe57756 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 12 Sep 2019 19:40:29 -0700 Subject: [PATCH 331/344] 1117 Building H2O --- 1116 Print Zero Even Odd.py | 7 ++- 1117 Building H2O.py | 122 ++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 1117 Building H2O.py diff --git a/1116 Print Zero Even Odd.py b/1116 Print Zero Even Odd.py index 209fad0..d7caee2 100644 --- a/1116 Print Zero Even Odd.py +++ b/1116 Print Zero Even Odd.py @@ -30,6 +30,7 @@ class ZeroEvenOdd { Input: n = 5 Output: "0102030405" """ +from typing import Callable from threading import Lock @@ -45,7 +46,7 @@ def __init__(self, n): self.locks[2].acquire() # printNumber(x) outputs "x", where x is an integer. - def zero(self, printNumber: 'Callable[[int], None]') -> None: + def zero(self, printNumber: Callable[[int], None]) -> None: for i in range(self.n): self.locks[0].acquire() printNumber(0) @@ -54,13 +55,13 @@ def zero(self, printNumber: 'Callable[[int], None]') -> None: else: self.locks[2].release() - def odd(self, printNumber: 'Callable[[int], None]') -> None: + def odd(self, printNumber: Callable[[int], None]) -> None: for i in range((self.n + 1) // 2): self.locks[1].acquire() printNumber(i * 2 + 1) self.locks[0].release() - def even(self, printNumber: 'Callable[[int], None]') -> None: + def even(self, printNumber: Callable[[int], None]) -> None: for i in range(self.n // 2): self.locks[2].acquire() printNumber(i * 2 + 2) diff --git a/1117 Building H2O.py b/1117 Building H2O.py new file mode 100644 index 0000000..a1da72e --- /dev/null +++ b/1117 Building H2O.py @@ -0,0 +1,122 @@ +#!/usr/bin/python3 +""" +There are two kinds of threads, oxygen and hydrogen. Your goal is to group these +threads to form water molecules. There is a barrier where each thread has to +wait until a complete molecule can be formed. Hydrogen and oxygen threads will +be given releaseHydrogen and releaseOxygen methods respectively, which will +allow them to pass the barrier. These threads should pass the barrier in groups +of three, and they must be able to immediately bond with each other to form a +water molecule. You must guarantee that all the threads from one molecule bond +before any other threads from the next molecule do. + +In other words: +If an oxygen thread arrives at the barrier when no hydrogen threads are +present, it has to wait for two hydrogen threads. +If a hydrogen thread arrives at the barrier when no other threads are present, +it has to wait for an oxygen thread and another hydrogen thread. +We don’t have to worry about matching the threads up explicitly; that is, the +threads do not necessarily know which other threads they are paired up with. The +key is just that threads pass the barrier in complete sets; thus, if we examine +the sequence of threads that bond and divide them into groups of three, each +group should contain one oxygen and two hydrogen threads. + +Write synchronization code for oxygen and hydrogen molecules that enforces these +constraints. + +Example 1: + +Input: "HOH" +Output: "HHO" +Explanation: "HOH" and "OHH" are also valid answers. +Example 2: + +Input: "OOHHHH" +Output: "HHOHHO" +Explanation: "HOHHHO", "OHHHHO", "HHOHOH", "HOHHOH", "OHHHOH", "HHOOHH", +"HOHOHH" and "OHHOHH" are also valid answers. + +Constraints: + +Total length of input string will be 3n, where 1 ≤ n ≤ 20. +Total number of H will be 2n in the input string. +Total number of O will be n in the input string. +""" +from typing import Callable +from threading import Semaphore + +from collections import deque + +class H2O: + def __init__(self): + self.hq = deque() + self.oq = deque() + + def hydrogen(self, releaseHydrogen: Callable[[], None]) -> None: + self.hq.append(releaseHydrogen) + self.try_output() + + def oxygen(self, releaseOxygen: Callable[[], None]) -> None: + self.oq.append(releaseOxygen) + self.try_output() + + def try_output(self): + if len(self.hq) >= 2 and len(self.oq) >= 1: + self.hq.popleft()() + self.hq.popleft()() + self.oq.popleft()() + + +class H2O_TLE2: + def __init__(self): + """ + Conditional Variable as counter? - Semaphore + """ + self.gates = [Semaphore(2), Semaphore(0)] # inititally allow 2 H, 0 O + + def hydrogen(self, releaseHydrogen: Callable[[], None]) -> None: + self.gates[0].acquire() + # releaseHydrogen() outputs "H". Do not change or remove this line. + releaseHydrogen() + if self.gates[0].acquire(blocking=False): # self.gates[0]._value > 0 + # still have available count + self.gates[0].release() + else: + self.gates[1].release() + + + def oxygen(self, releaseOxygen: Callable[[], None]) -> None: + self.gates[1].acquire() + # releaseOxygen() outputs "O". Do not change or remove this line. + releaseOxygen() + self.gates[0].release() + self.gates[0].release() + + +class H2O_TLE: + def __init__(self): + """ + Conditional Variable as counter? + Fixed at HHO pattern + """ + self.h_cnt = 0 + self.locks = [Lock() for _ in range(3)] + self.locks[1].acquire() + + + def hydrogen(self, releaseHydrogen: Callable[[], None]) -> None: + self.locks[0].acquire() + self.h_cnt += 1 + # releaseHydrogen() outputs "H". Do not change or remove this line. + releaseHydrogen() + if self.h_cnt < 2: + self.locks[0].release() + else: + self.locks[1].release() + + + def oxygen(self, releaseOxygen: Callable[[], None]) -> None: + self.locks[1].acquire() + # releaseOxygen() outputs "O". Do not change or remove this line. + releaseOxygen() + self.h_cnt = 0 + self.locks[0].release() From c1abde0fcdc1f5cdaabd9cfe076048c7a1f87c22 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 12 Sep 2019 22:38:12 -0700 Subject: [PATCH 332/344] spiral matrix --- 053 Spiral Matrix.py | 134 +++++++++++++++++------------- 058 Spiral Matrix II.py | 152 +++++++++++++++++++++------------- 068 Text Justification py3.py | 3 +- 697 Degree of an Array.py | 4 +- 4 files changed, 175 insertions(+), 118 deletions(-) diff --git a/053 Spiral Matrix.py b/053 Spiral Matrix.py index 9897659..4424d6a 100644 --- a/053 Spiral Matrix.py +++ b/053 Spiral Matrix.py @@ -1,59 +1,75 @@ -""" -Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order. - -For example, -Given the following matrix: - -[ - [ 1, 2, 3 ], - [ 4, 5, 6 ], - [ 7, 8, 9 ] -] -You should return [1,2,3,6,9,8,7,4,5]. -""" -__author__ = 'Danyang' -class Solution: - def spiralOrder(self, matrix): - """ - top - | - left --+-- right - | - bottom - - be careful with the index - - :param matrix: a list of lists of integers - :return: a list of integers - """ - if not matrix or not matrix[0]: - return matrix - - result = [] - - left = 0 - right = len(matrix[0])-1 - top = 0 - bottom = len(matrix)-1 - - while left<=right and top<=bottom: - for i in xrange(left, right+1): - result.append(matrix[top][i]) - for i in xrange(top+1, bottom+1): - result.append(matrix[i][right]) - for i in reversed(xrange(left+1, right)): - if top List[str]: Round robin distribution of spaces Look before jump + Look before you leap """ ret = [] char_cnt = 0 @@ -106,7 +107,7 @@ def fullJustify(self, words: List[str], maxWidth: int) -> List[str]: # break, move w into the next line # Round robin distribut the spaces except for the last word for i in range(maxWidth - char_cnt): - cur_words[i % max(1, len(cur_words) - 1)] += " " + cur_words[i % max(1, len(cur_words) - 1)] += " " # insert in between # len(cur_words) - 1 can be 0 ret.append("".join(cur_words)) diff --git a/697 Degree of an Array.py b/697 Degree of an Array.py index fef11be..8ef5c98 100644 --- a/697 Degree of an Array.py +++ b/697 Degree of an Array.py @@ -36,15 +36,17 @@ def findShortestSubArray(self, nums: List[int]) -> int: return counter = defaultdict(int) - first = {} + first = {} # map from number to index mx = [0, 0] # [degree, length] for i, n in enumerate(nums): if n not in first: first[n] = i # setdefault counter[n] += 1 if counter[n] > mx[0]: + # If there is only one mode number mx = [counter[n], i - first[n] + 1] elif counter[n] == mx[0]: + # How to handle duplicate mode number mx[1] = min(mx[1], i - first[n] + 1) return mx[1] From 3fb14aeea62a960442e47dfde9f964c7ffce32be Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 12 Sep 2019 23:23:37 -0700 Subject: [PATCH 333/344] 224 Basic Calculator py3 --- 224 Basic Calculator py3.py | 67 +++++++++++++++++++++++++++++++++++++ 772 Basic Calculator III.py | 26 +++++++------- 2 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 224 Basic Calculator py3.py diff --git a/224 Basic Calculator py3.py b/224 Basic Calculator py3.py new file mode 100644 index 0000000..74a43a3 --- /dev/null +++ b/224 Basic Calculator py3.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +Implement a basic calculator to evaluate a simple expression string. + +The expression string may contain open ( and closing parentheses ), the plus + +or minus sign -, non-negative integers and empty spaces . + +Example 1: + +Input: "1 + 1" +Output: 2 +Example 2: + +Input: " 2-1 + 2 " +Output: 3 +Example 3: + +Input: "(1+(4+5+2)-3)+(6+8)" +Output: 23 +Note: +You may assume that the given expression is always valid. +Do not use the eval built-in library function. +""" +from typing import List + + +class Solution: + def calculate(self, s: str) -> int: + """ + 1. treat +/- as unary operator + 2. maintain stk of operands to sum + 3. handle bracket recursively + """ + ret, _ = self.eval(s + "\0", 0, []) + return ret + + def eval(self, s: str, start: int, stk: List[int]) -> int: + prev_op = "+" + operand = 0 + i = start + while i < len(s): # not using for-loop, since the cursor needs to advance in recursion + if s[i] == " ": + pass + elif s[i].isdigit(): + operand = operand * 10 + int(s[i]) + elif s[i] in ("+", "-", ")", "\0"): # delimited + if prev_op == "+": + stk.append(operand) + elif prev_op == "-": + stk.append(-operand) + + if s[i] in ("+", "-"): + operand = 0 + prev_op = s[i] + elif s[i] in (")", "\0"): + return sum(stk), i + elif s[i] == "(": + # avoid setting operand to 0 + operand, i = self.eval(s, i + 1, []) + else: + raise + + i += 1 + + +if __name__ == "__main__": + assert Solution().calculate("(1+(4+5+2)-3)+(6+8)") == 23 diff --git a/772 Basic Calculator III.py b/772 Basic Calculator III.py index 4e659c9..9d41e5a 100644 --- a/772 Basic Calculator III.py +++ b/772 Basic Calculator III.py @@ -30,20 +30,21 @@ def calculate(self, s: str) -> int: """ s = s + "\0" # signal the end ret, _ = self.eval(s, 0, []) - print(ret) return ret def eval(self, s, i, stk): + """ + return the cursor since the cursor advances in recursion + """ operand = 0 prev_op = "+" while i < len(s): c = s[i] if c == " ": - i += 1 + pass # not continue since need trigger i += 1 elif c.isdigit(): operand = operand * 10 + int(c) - i += 1 - elif c in ("+", "-", "*", "/", ")", "\0"): # delimited + elif c in ("+", "-", "*", "/", ")", "\0"): # delimiter if prev_op == "+": stk.append(operand) elif prev_op == "-": @@ -55,22 +56,19 @@ def eval(self, s, i, stk): prev_operand = stk.pop() stk.append(int(prev_operand / operand)) - operand = 0 - prev_op = c - i += 1 - - if c == ")": + if c in ("+", "-", "*", "/"): + operand = 0 + prev_op = c + elif c in (")", "\0"): return sum(stk), i - - elif c == "(": + elif c == "(": # "(" is not delimiter operand, i = self.eval(s, i + 1, []) - # elif c == ")": - # return sum(stk), i + 1 else: raise - return sum(stk), i + i += 1 if __name__ == "__main__": + assert Solution().calculate("(( ( ( 4- 2)+ ( 6+ 10 ) )+ 1) /( ( ( 7 + 9 )* ( 5*8) )- ( 5 + ( 2 * 10 ) ) ) )") == 0 assert Solution().calculate("(2+6* 3+5- (3*14/7+2)*5)+3") == -12 From 0dc4c62d1c8605619590aa37b3738c02e958f0dc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 15 Nov 2023 15:15:19 -0500 Subject: [PATCH 334/344] 2915 --- ...Longest Subsequence That Sums to Target.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 2915 Length of the Longest Subsequence That Sums to Target.py diff --git a/2915 Length of the Longest Subsequence That Sums to Target.py b/2915 Length of the Longest Subsequence That Sums to Target.py new file mode 100644 index 0000000..36cfc48 --- /dev/null +++ b/2915 Length of the Longest Subsequence That Sums to Target.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 +""" +You are given a 0-indexed array of integers nums, and an integer target. + +Return the length of the longest subsequence of nums that sums up to target. If no such subsequence exists, return -1. + +A subsequence is an array that can be derived from another array by deleting some or no elements without changing the order of the remaining elements. + + + +Example 1: + +Input: nums = [1,2,3,4,5], target = 9 +Output: 3 +Explanation: There are 3 subsequences with a sum equal to 9: [4,5], [1,3,5], and [2,3,4]. The longest subsequences are [1,3,5], and [2,3,4]. Hence, the answer is 3. +Example 2: + +Input: nums = [4,1,3,2,1,5], target = 7 +Output: 4 +Explanation: There are 5 subsequences with a sum equal to 7: [4,3], [4,1,2], [4,2,1], [1,1,5], and [1,3,2,1]. The longest subsequence is [1,3,2,1]. Hence, the answer is 4. +Example 3: + +Input: nums = [1,1,5,4,5], target = 3 +Output: -1 +Explanation: It can be shown that nums has no subsequence that sums up to 3. + + +Constraints: + +1 <= nums.length <= 1000 +1 <= nums[i] <= 1000 +1 <= target <= 1000 +""" + + +class Solution: + def lengthOfLongestSubsequence(self, nums: List[int], target: int) -> int: + cur = {0: 0, nums[0]: 1} + for i in range(1, len(nums)): + nxt = {} + for sm, l in cur.items(): + # Take + sm_nxt = sm + nums[i] + if sm_nxt > target: + # prune + continue + + l_nxt = l + 1 + if sm_nxt in cur: + l_nxt = max(l_nxt, cur[sm_nxt]) + if sm_nxt in nxt: + l_nxt = max(l_nxt, nxt[sm_nxt]) + nxt[sm_nxt] = l_nxt + + # Not take, merge + for sm, l in cur.items(): + if sm not in nxt: + nxt[sm] = l + + cur = nxt + + if target in cur: + return cur[target] + + return -1 \ No newline at end of file From e45cf8f2d30cbfaede94343c2a0cf349d09f09f6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 12 Nov 2024 10:32:41 -0500 Subject: [PATCH 335/344] add --- 1001 Grid Illumination.py | 4 + ...ck If Word Is Valid After Substitutions.py | 77 ++++++++++++++++ 1006 Clumsy Factorial.py | 67 ++++++++++++++ ... Minimum Domino Rotations For Equal Row.py | 59 +++++++++++++ 1015 Smallest Integer Divisible by K.py | 57 ++++++++++++ ...ing With Substrings Representing 1 To N.py | 48 ++++++++++ 1034 Coloring A Border.py | 87 +++++++++++++++++++ 351 Android Unlock Patterns py3.py | 38 ++++++++ 588 Design In-Memory File System.py | 74 ++++++++++++++++ 609 Find Duplicate File in System.py | 0 843 Guess the Word.py | 54 ++++++++++++ 11 files changed, 565 insertions(+) create mode 100644 1001 Grid Illumination.py create mode 100644 1003 Check If Word Is Valid After Substitutions.py create mode 100644 1006 Clumsy Factorial.py create mode 100644 1007 Minimum Domino Rotations For Equal Row.py create mode 100644 1015 Smallest Integer Divisible by K.py create mode 100644 1016 Binary String With Substrings Representing 1 To N.py create mode 100644 1034 Coloring A Border.py create mode 100644 351 Android Unlock Patterns py3.py create mode 100644 588 Design In-Memory File System.py create mode 100644 609 Find Duplicate File in System.py create mode 100644 843 Guess the Word.py diff --git a/1001 Grid Illumination.py b/1001 Grid Illumination.py new file mode 100644 index 0000000..ed58566 --- /dev/null +++ b/1001 Grid Illumination.py @@ -0,0 +1,4 @@ +#!/usr/bin/python3 +""" + +""" diff --git a/1003 Check If Word Is Valid After Substitutions.py b/1003 Check If Word Is Valid After Substitutions.py new file mode 100644 index 0000000..85bd5ff --- /dev/null +++ b/1003 Check If Word Is Valid After Substitutions.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 +""" +Given a string s, determine if it is valid. + +A string s is valid if, starting with an empty string t = "", you can transform t into s after performing the following operation any number of times: + +Insert string "abc" into any position in t. More formally, t becomes tleft + "abc" + tright, where t == tleft + tright. Note that tleft and tright may be empty. +Return true if s is a valid string, otherwise, return false. + + + +Example 1: + +Input: s = "aabcbc" +Output: true +Explanation: +"" -> "abc" -> "aabcbc" +Thus, "aabcbc" is valid. +Example 2: + +Input: s = "abcabcababcc" +Output: true +Explanation: +"" -> "abc" -> "abcabc" -> "abcabcabc" -> "abcabcababcc" +Thus, "abcabcababcc" is valid. +Example 3: + +Input: s = "abccba" +Output: false +Explanation: It is impossible to get "abccba" using the operation. + + +Constraints: + +1 <= s.length <= 2 * 104 +s consists of letters 'a', 'b', and 'c' +""" + +from collections import defaultdict + +class Solution: + def isValid_naive(self, s: str) -> bool: + """ + similar to valid parenthesis of () + cnt[a] >= cnt[b] >= cnt[c] + in the end, cnt[a] == cnt[b] == cnt[c] + but aaabbbccc is invalid + + remove "abc", there another "abc" in the string. O(N^2) + """ + if len(s) == 0: + return True + + for i in range(len(s) - 2): + if s[i:i+3] == "abc": + return self.isValid(s[:i] + s[i+3:]) + + return False + + def isValid(self, s: str) -> bool: + """ + when c, we there must be a and b immediately before + using stk + """ + stk = [] + for e in s: + if e in ("a", "b"): + stk.append(e) + else: # "c" + if len(stk) < 2: + return False + if stk.pop() != "b": + return False + if stk.pop() != "a": + return False + + return len(stk) == 0 \ No newline at end of file diff --git a/1006 Clumsy Factorial.py b/1006 Clumsy Factorial.py new file mode 100644 index 0000000..7cd1fe8 --- /dev/null +++ b/1006 Clumsy Factorial.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +The factorial of a positive integer n is the product of all positive integers less than or equal to n. + +For example, factorial(10) = 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1. +We make a clumsy factorial using the integers in decreasing order by swapping out the multiply operations for a fixed rotation of operations with multiply '*', divide '/', add '+', and subtract '-' in this order. + +For example, clumsy(10) = 10 * 9 / 8 + 7 - 6 * 5 / 4 + 3 - 2 * 1. +However, these operations are still applied using the usual order of operations of arithmetic. We do all multiplication and division steps before any addition or subtraction steps, and multiplication and division steps are processed left to right. + +Additionally, the division that we use is floor division such that 10 * 9 / 8 = 90 / 8 = 11. + +Given an integer n, return the clumsy factorial of n. + + + +Example 1: + +Input: n = 4 +Output: 7 +Explanation: 7 = 4 * 3 / 2 + 1 +Example 2: + +Input: n = 10 +Output: 12 +Explanation: 12 = 10 * 9 / 8 + 7 - 6 * 5 / 4 + 3 - 2 * 1 + + +Constraints: + +1 <= n <= 104 +""" + +class Solution: + def clumsy(self, n: int) -> int: + """ + * / + - + + is applied, + - the next set of * / + """ + accu = 0 + for i in range(min(4, n)): + num = n - i + if i % 4 == 0: + accu += num + elif i % 4 == 1: + accu *= num + elif i % 4 == 2: + accu //= num + else: + accu += num + + cur = 0 + for i in range(4, n): + num = n - i + if i % 4 == 0: + cur += num + elif i % 4 == 1: + cur *= num + elif i % 4 == 2: + cur //= num + else: + accu += num + accu -= cur + cur = 0 + + return accu - cur # remaining cur \ No newline at end of file diff --git a/1007 Minimum Domino Rotations For Equal Row.py b/1007 Minimum Domino Rotations For Equal Row.py new file mode 100644 index 0000000..9b1e6ae --- /dev/null +++ b/1007 Minimum Domino Rotations For Equal Row.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 +""" +In a row of dominoes, tops[i] and bottoms[i] represent the top and bottom halves of the ith domino. (A domino is a tile with two numbers from 1 to 6 - one on each half of the tile.) + +We may rotate the ith domino, so that tops[i] and bottoms[i] swap values. + +Return the minimum number of rotations so that all the values in tops are the same, or all the values in bottoms are the same. + +If it cannot be done, return -1. + + +Example 1: + + +Input: tops = [2,1,2,4,2,2], bottoms = [5,2,6,2,3,2] +Output: 2 +Explanation: +The first figure represents the dominoes as given by tops and bottoms: before we do any rotations. +If we rotate the second and fourth dominoes, we can make every value in the top row equal to 2, as indicated by the second figure. +Example 2: + +Input: tops = [3,5,1,2,3], bottoms = [3,6,3,3,4] +Output: -1 +Explanation: +In this case, it is not possible to rotate the dominoes to make one row of values equal. + + +Constraints: + +2 <= tops.length <= 2 * 104 +bottoms.length == tops.length +1 <= tops[i], bottoms[i] <= 6 +""" +class Solution: + def minDominoRotations(self, tops: List[int], bottoms: List[int]) -> int: + """ + Mainly focus on making tops the same + bottoms check can be done by swapping the params + + find target first and then swap + """ + return min(self.find_min(tops, bottoms), self.find_min(bottoms, tops)) + + def find_min(self, tops, bottoms): + targets = set([tops[0], bottoms[0]]) + N = len(tops) + for i in range(1, N): + targets &= set([tops[i], bottoms[i]]) + + if len(targets) == 0: + return -1 + + target = targets.pop() + swap = 0 + for i in range(N): + if target != tops[i]: + swap += 1 + + return swap \ No newline at end of file diff --git a/1015 Smallest Integer Divisible by K.py b/1015 Smallest Integer Divisible by K.py new file mode 100644 index 0000000..2d1dd1f --- /dev/null +++ b/1015 Smallest Integer Divisible by K.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +""" +Given a positive integer k, you need to find the length of the smallest positive integer n such that n is divisible by k, and n only contains the digit 1. + +Return the length of n. If there is no such n, return -1. + +Note: n may not fit in a 64-bit signed integer. + + + +Example 1: + +Input: k = 1 +Output: 1 +Explanation: The smallest answer is n = 1, which has length 1. +Example 2: + +Input: k = 2 +Output: -1 +Explanation: There is no such positive integer n divisible by 2. +Example 3: + +Input: k = 3 +Output: 3 +Explanation: The smallest answer is n = 111, which has length 3. + + +Constraints: + +1 <= k <= 10^5 +""" +from collections import defaultdict + + +class Solution: + def smallestRepunitDivByK(self, k: int) -> int: + """ + 1 % k = 1 + 11 % k = prev * 10 + 1 % k + 111 % k = prev * 10 + 1 % k + """ + if k % 2 == 0 or k % 5 == 0: + return -1 + + hit = defaultdict(bool) + l = 1 + cur = 1 + remainder = 1 % k + while True: + if hit[remainder]: + return -1 + if remainder == 0: + return l + + hit[remainder] = True + remainder = (remainder * 10 + 1) % k + l += 1 diff --git a/1016 Binary String With Substrings Representing 1 To N.py b/1016 Binary String With Substrings Representing 1 To N.py new file mode 100644 index 0000000..e360018 --- /dev/null +++ b/1016 Binary String With Substrings Representing 1 To N.py @@ -0,0 +1,48 @@ +#!/usr/bin/python3 +""" +Given a binary string s and a positive integer n, return true if the binary representation of all the integers in the range [1, n] are substrings of s, or false otherwise. + +A substring is a contiguous sequence of characters within a string. + +Example 1: + +Input: s = "0110", n = 3 +Output: true +Example 2: + +Input: s = "0110", n = 4 +Output: false + + +Constraints: + +1 <= s.length <= 1000 +s[i] is either '0' or '1'. +1 <= n <= 10^9 +""" + +class Solution: + def queryString(self, s: str, n: int) -> bool: + """ + Naive solution: KMP string matching from 1 to N, O(N * (m + n)). + Python `in` is enough + + Construct numbers from the string s itself + Scan the s from left to right + """ + numbers = set() + for i in range(len(s)): + cur = 0 + sig = 1 + for j in range(i, len(s)): + if s[~j] == "1": + cur += sig + if cur > n: + break + + sig <<= 1 + + if cur != 0: + numbers.add(cur) + + return len(numbers) == n diff --git a/1034 Coloring A Border.py b/1034 Coloring A Border.py new file mode 100644 index 0000000..28a399c --- /dev/null +++ b/1034 Coloring A Border.py @@ -0,0 +1,87 @@ +#!/usr/bin/python3 +""" +You are given an m x n integer matrix grid, and three integers row, col, and color. Each value in the grid represents the color of the grid square at that location. + +Two squares are called adjacent if they are next to each other in any of the 4 directions. + +Two squares belong to the same connected component if they have the same color and they are adjacent. + +The border of a connected component is all the squares in the connected component that are either adjacent to (at least) a square not in the component, or on the boundary of the grid (the first or last row or column). + +You should color the border of the connected component that contains the square grid[row][col] with color. + +Return the final grid. + + + +Example 1: + +Input: grid = [[1,1],[1,2]], row = 0, col = 0, color = 3 +Output: [[3,3],[3,2]] +Example 2: + +Input: grid = [[1,2,2],[2,3,2]], row = 0, col = 1, color = 3 +Output: [[1,3,3],[2,3,3]] +Example 3: + +Input: grid = [[1,1,1],[1,1,1],[1,1,1]], row = 1, col = 1, color = 2 +Output: [[2,2,2],[2,1,2],[2,2,2]] + + +Constraints: + +m == grid.length +n == grid[i].length +1 <= m, n <= 50 +1 <= grid[i][j], color <= 1000 +0 <= row < m +0 <= col < n +""" + + +class Solution: + def __init__(self): + self.dirs = [(0, 1), (0, -1), (1, 0), (-1, 0)] + + def colorBorder(self, grid: List[List[int]], row: int, col: int, color: int) -> List[List[int]]: + """ + In any direction, it has other color then color it + in four direction, same color, then not color + """ + origin = grid[row][col] + m = len(grid) + n = len(grid[0]) + visited = [ + [False for j in range(n)] + for i in range(m) + ] + should_color = [ + [False for j in range(n)] + for i in range(m) + ] + self.dfs(grid, row, col, visited, should_color) + + for i in range(m): + for j in range(n): + if should_color[i][j]: + grid[i][j] = color + + return grid + + def dfs(self, grid, i, j, visited, should_color): + m = len(grid) + n = len(grid[0]) + + visited[i][j] = True + cnt = 0 + for d in self.dirs: + I = i + d[0] + J = j + d[1] + if 0 <= I < m and 0 <= J < n: + if not visited[I][J] and grid[I][J] == grid[i][j]: + self.dfs(grid, I, J, visited, should_color) + + if grid[I][J] == grid[i][j]: + cnt += 1 + if cnt < 4: + should_color[i][j] = True diff --git a/351 Android Unlock Patterns py3.py b/351 Android Unlock Patterns py3.py new file mode 100644 index 0000000..15dbc11 --- /dev/null +++ b/351 Android Unlock Patterns py3.py @@ -0,0 +1,38 @@ +#!/usr/bin/python3 +""" +Given an Android 3x3 key lock screen and two integers m and n, where 1 ≤ m ≤ n +≤ 9, count the total number of unlock patterns of the Android lock screen, which +consist of minimum of m keys and maximum n keys. + +Rules for a valid pattern: + +Each pattern must connect at least m keys and at most n keys. +All the keys must be distinct. +If the line connecting two consecutive keys in the pattern passes through any +other keys, the other keys must have previously selected in the pattern. No +jumps through non selected key is allowed. +The order of keys used matters. + + + + + +Explanation: + +| 1 | 2 | 3 | +| 4 | 5 | 6 | +| 7 | 8 | 9 | +Invalid move: 4 - 1 - 3 - 6 +Line 1 - 3 passes through key 2 which had not been selected in the pattern. + +Invalid move: 4 - 1 - 9 - 2 +Line 1 - 9 passes through key 5 which had not been selected in the pattern. + +Valid move: 2 - 4 - 1 - 3 - 6 +Line 1 - 3 is valid because it passes through key 2, which had been selected in +the pattern + +Valid move: 6 - 5 - 4 - 1 - 9 - 2 +Line 1 - 9 is valid because it passes through key 5, which had been selected in +the pattern. +""" diff --git a/588 Design In-Memory File System.py b/588 Design In-Memory File System.py new file mode 100644 index 0000000..31b4fb0 --- /dev/null +++ b/588 Design In-Memory File System.py @@ -0,0 +1,74 @@ +#!/usr/bin/python3 +""" +Design an in-memory file system to simulate the following functions: + +ls: Given a path in string format. If it is a file path, return a list that only +contains this file's name. If it is a directory path, return the list of file +and directory names in this directory. Your output (file and directory names +together) should in lexicographic order. + +mkdir: Given a directory path that does not exist, you should make a new +directory according to the path. If the middle directories in the path don't +exist either, you should create them as well. This function has void return type. + +addContentToFile: Given a file path and file content in string format. If the +file doesn't exist, you need to create that file containing given content. If +the file already exists, you need to append given content to original content. +This function has void return type. + +readContentFromFile: Given a file path, return its content in string format. + + + +Example: + +Input: +["FileSystem","ls","mkdir","addContentToFile","ls","readContentFromFile"] +[[],["/"],["/a/b/c"],["/a/b/c/d","hello"],["/"],["/a/b/c/d"]] + +Output: +[null,[],null,null,["a"],"hello"] + +Explanation: +filesystem + + +Note: +You can assume all file or directory paths are absolute paths which begin with +/ and do not end with / except that the path is just "/". +You can assume that all operations will be passed valid parameters and users +will not attempt to retrieve file content or list a directory or file that does +not exist. +You can assume that all directory names and file names only contain lower-case +letters, and same names won't exist in the same directory. +""" +from typing import List + + +class FileSystem: + def __init__(self): + """ + n-ary tree (trie) + sort then sort every time + """ + + + def ls(self, path: str) -> List[str]: + + + def mkdir(self, path: str) -> None: + + + def addContentToFile(self, filePath: str, content: str) -> None: + + + def readContentFromFile(self, filePath: str) -> str: + + + +# Your FileSystem object will be instantiated and called as such: +# obj = FileSystem() +# param_1 = obj.ls(path) +# obj.mkdir(path) +# obj.addContentToFile(filePath,content) +# param_4 = obj.readContentFromFile(filePath) diff --git a/609 Find Duplicate File in System.py b/609 Find Duplicate File in System.py new file mode 100644 index 0000000..e69de29 diff --git a/843 Guess the Word.py b/843 Guess the Word.py new file mode 100644 index 0000000..e6f703f --- /dev/null +++ b/843 Guess the Word.py @@ -0,0 +1,54 @@ +#!/usr/bin/python3 +""" +This problem is an interactive problem new to the LeetCode platform. + +We are given a word list of unique words, each word is 6 letters long, and one +word in this list is chosen as secret. + +You may call master.guess(word) to guess a word. The guessed word should have +type string and must be from the original list with 6 lowercase letters. + +This function returns an integer type, representing the number of exact matches +(value and position) of your guess to the secret word. Also, if your guess is +not in the given wordlist, it will return -1 instead. + +For each test case, you have 10 guesses to guess the word. At the end of any +number of calls, if you have made 10 or less calls to master.guess and at least +one of these guesses was the secret, you pass the testcase. + +Besides the example test case below, there will be 5 additional test cases, each +with 100 words in the word list. The letters of each word in those testcases +were chosen independently at random from 'a' to 'z', such that every word in the +given word lists is unique. + +Example 1: +Input: secret = "acckzz", wordlist = ["acckzz","ccbazz","eiowzz","abcczz"] + +Explanation: + +master.guess("aaaaaa") returns -1, because "aaaaaa" is not in wordlist. +master.guess("acckzz") returns 6, because "acckzz" is secret and has all 6 +matches. +master.guess("ccbazz") returns 3, because "ccbazz" has 3 matches. +master.guess("eiowzz") returns 2, because "eiowzz" has 2 matches. +master.guess("abcczz") returns 4, because "abcczz" has 4 matches. + +We made 5 calls to master.guess and one of them was the secret, so we pass the +test case. +""" + +""" +This is Master's API interface. +You should not implement it, or speculate about its implementation +""" +class Master: + def guess(self, word: str) -> int: + pass + + +class Solution: + def findSecretWord(self, wordlist: List[str], master: Master) -> None: + """ + 10 guesses + at least one of these guesses was the secret, you pass the testcase. + """ From 077a0e3e585b0f2516a5f9fa85b20760911cf6c7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 26 Nov 2024 22:56:39 -0500 Subject: [PATCH 336/344] update --- 1033 Moving Stones Until Consecutive.py | 57 +++++++ ... Binary Search Tree to Greater Sum Tree.py | 53 ++++++ 1040 Moving Stones Until Consecutive II.py | 158 ++++++++++++++++++ ...uence of Strings Appeared on the Screen.py | 59 +++++++ 3331 Find Subtree Sizes After Changes.py | 147 ++++++++++++++++ 3355 Zero Array Transformation I.py | 88 ++++++++++ 3356 Zero Array Transformation II.py | 112 +++++++++++++ 7 files changed, 674 insertions(+) create mode 100644 1033 Moving Stones Until Consecutive.py create mode 100644 1038 Binary Search Tree to Greater Sum Tree.py create mode 100644 1040 Moving Stones Until Consecutive II.py create mode 100644 3324 Find the Sequence of Strings Appeared on the Screen.py create mode 100644 3331 Find Subtree Sizes After Changes.py create mode 100644 3355 Zero Array Transformation I.py create mode 100644 3356 Zero Array Transformation II.py diff --git a/1033 Moving Stones Until Consecutive.py b/1033 Moving Stones Until Consecutive.py new file mode 100644 index 0000000..76e4238 --- /dev/null +++ b/1033 Moving Stones Until Consecutive.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +""" +There are three stones in different positions on the X-axis. You are given three integers a, b, and c, the positions of the stones. + +In one move, you pick up a stone at an endpoint (i.e., either the lowest or highest position stone), and move it to an unoccupied position between those endpoints. Formally, let's say the stones are currently at positions x, y, and z with x < y < z. You pick up the stone at either position x or position z, and move that stone to an integer position k, with x < k < z and k != y. + +The game ends when you cannot make any more moves (i.e., the stones are in three consecutive positions). + +Return an integer array answer of length 2 where: + +answer[0] is the minimum number of moves you can play, and +answer[1] is the maximum number of moves you can play. + + +Example 1: + +Input: a = 1, b = 2, c = 5 +Output: [1,2] +Explanation: Move the stone from 5 to 3, or move the stone from 5 to 4 to 3. +Example 2: + +Input: a = 4, b = 3, c = 2 +Output: [0,0] +Explanation: We cannot make any moves. +Example 3: + +Input: a = 3, b = 5, c = 1 +Output: [1,2] +Explanation: Move the stone from 1 to 4; or move the stone from 1 to 2 to 4. + + +Constraints: + +1 <= a, b, c <= 100 +a, b, and c have different values. +""" +class Solution: + def numMovesStones(self, a: int, b: int, c: int) -> List[int]: + """ + max: move 1 slot by 1 slot + min: move the entire gap. + """ + A = [a, b, c] + A.sort() + gap_l = A[1] - A[0] - 1 + gap_r = A[2] - A[1] - 1 + maxa = gap_l + gap_r + + min_gap = min(gap_l, gap_r) + if gap_l == 0 and gap_r == 0: + mini = 0 + elif min_gap == 0 or min_gap == 1: + mini = 1 + else: + mini = 2 + + return mini, maxa \ No newline at end of file diff --git a/1038 Binary Search Tree to Greater Sum Tree.py b/1038 Binary Search Tree to Greater Sum Tree.py new file mode 100644 index 0000000..49598cc --- /dev/null +++ b/1038 Binary Search Tree to Greater Sum Tree.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +""" +Given the root of a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original BST is changed to the original key plus the sum of all keys greater than the original key in BST. + +As a reminder, a binary search tree is a tree that satisfies these constraints: + +The left subtree of a node contains only nodes with keys less than the node's key. +The right subtree of a node contains only nodes with keys greater than the node's key. +Both the left and right subtrees must also be binary search trees. + + +Example 1: + + +Input: root = [4,1,6,0,2,5,7,null,null,null,3,null,null,null,8] +Output: [30,36,21,36,35,26,15,null,null,null,33,null,null,null,8] +Example 2: + +Input: root = [0,null,1] +Output: [1,null,1] + + +Constraints: + +The number of nodes in the tree is in the range [1, 100]. +0 <= Node.val <= 100 +All the values in the tree are unique. +""" + + +class Solution: + def __init__(self): + self.accu = 0 + + def bstToGst(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + """ + sum the right subtree + BST: to print in order (asc), in-order traversal + to sum greater keys: mirroed in-order traversal + + Build a sample sum tree to find pattern + """ + self.update_and_sum(root) + return root + + def update_and_sum(self, cur): + if cur is None: + return + + self.update_and_sum(cur.right) + cur.val += self.accu + self.accu = cur.val + self.update_and_sum(cur.left) \ No newline at end of file diff --git a/1040 Moving Stones Until Consecutive II.py b/1040 Moving Stones Until Consecutive II.py new file mode 100644 index 0000000..8f9f7e1 --- /dev/null +++ b/1040 Moving Stones Until Consecutive II.py @@ -0,0 +1,158 @@ +#!/usr/bin/python3 +""" +There are some stones in different positions on the X-axis. You are given an integer array stones, the positions of the stones. + +Call a stone an endpoint stone if it has the smallest or largest position. In one move, you pick up an endpoint stone and move it to an unoccupied position so that it is no longer an endpoint stone. + +In particular, if the stones are at say, stones = [1,2,5], you cannot move the endpoint stone at position 5, since moving it to any position (such as 0, or 3) will still keep that stone as an endpoint stone. +The game ends when you cannot make any more moves (i.e., the stones are in three consecutive positions). + +Return an integer array answer of length 2 where: + +answer[0] is the minimum number of moves you can play, and +answer[1] is the maximum number of moves you can play. + + +Example 1: + +Input: stones = [7,4,9] +Output: [1,2] +Explanation: We can move 4 -> 8 for one move to finish the game. +Or, we can move 9 -> 5, 4 -> 6 for two moves to finish the game. +Example 2: + +Input: stones = [6,5,4,3,10] +Output: [2,3] +Explanation: We can move 3 -> 8 then 10 -> 7 to finish the game. +Or, we can move 3 -> 7, 4 -> 8, 5 -> 9 to finish the game. +Notice we cannot move 10 -> 2 to finish the game, because that would be an illegal move. + + +Constraints: + +3 <= stones.length <= 10^4 +1 <= stones[i] <= 10^9 +All the values of stones are unique. +""" +class Solution: + def numMovesStonesII(self, stones: List[int]) -> List[int]: + A = sorted(stones) + n = len(A) + # Calculate Max: + # move every endpoint stone to every hole + hi = max( + # move A[0] + A[~0] - A[1] + 1 - (n - 1), + # move A[~0] + A[~1] - A[0] + 1 - (n - 1), + ) + + # Calcualte Min: + # sliding window + lo = n + i = 0 + for j in range(n): + while i < j and A[j] - A[i] + 1 > n: + i += 1 + + total_slots = A[j] - A[i] + 1 + existing = j - i + 1 + + if total_slots == n - 1 and existing == n - 1: + # edge case: [1, 2, 3, 10] + lo = min(lo, 2) + else: + # move stones outside the window into it or adjacent to it + lo = min(lo, n - existing) + + return lo, hi + + +from collections import deque + + +class SolutionWrongTLE: + def numMovesStonesII(self, stones: List[int]) -> List[int]: + """ + calculate gap array + max = sum(gaps)? Has to move it s.t. no longer endpoint + greedy, move to max / min gap + greedy, move to elimate the max / min gap at two ends + + To get max + greedy, move the min gap, to the other end, so the other end has better chance + to become min gap + + To get min + greedy, move the max gap, to the other end, so the other end's other end has + better chance become max gap + """ + stones.sort() + gaps = [stones[i + 1] - stones[i] - 1 for i in range(len(stones) - 1)] + + # to get max, pop the min gap + d = deque(gaps) + cnt = 0 + while d: + while d and d[0] == 0: + d.popleft() + while d and d[~0] == 0: + d.pop() + + if not d: + break + if len(d) == 1: + cnt += d[0] + d[0] = 0 + elif d[0] > d[~0]: + d.pop() + cnt += 1 + d[0] -= 1 + if d[0] > 0: + d[0] -= 1 + d.appendleft(1) + else: + cnt += 1 + d.popleft() + d[~0] -= 1 + if d[~0] > 0: + d[~0] -= 1 + d.append(1) + + # to get min, pop the max gap + d = deque(gaps) + cnt2 = 0 + while d: + while d and d[0] == 0: + d.popleft() + while d and d[~0] == 0: + d.pop() + if not d: + break + if len(d) == 1: + if d[0] >= 2: + cnt2 += 2 + else: + cnt2 += 1 + d[0] = 0 + elif d[0] > d[~0]: + d.popleft() + cnt2 += 1 + d[~0] -= 1 + if d[~0] > 0: + d[~0] -= 1 + d.append(1) + else: + cnt2 += 1 + d.pop() + d[0] -= 1 + if d[0] > 0: + d[0] -= 1 + d.appendleft(1) + + return cnt2, cnt + + + + + diff --git a/3324 Find the Sequence of Strings Appeared on the Screen.py b/3324 Find the Sequence of Strings Appeared on the Screen.py new file mode 100644 index 0000000..b003423 --- /dev/null +++ b/3324 Find the Sequence of Strings Appeared on the Screen.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 +""" +You are given a string target. + +Alice is going to type target on her computer using a special keyboard that has only two keys: + +Key 1 appends the character "a" to the string on the screen. +Key 2 changes the last character of the string on the screen to its next character in the English alphabet. For example, "c" changes to "d" and "z" changes to "a". +Note that initially there is an empty string "" on the screen, so she can only press key 1. + +Return a list of all strings that appear on the screen as Alice types target, in the order they appear, using the minimum key presses. + + + +Example 1: + +Input: target = "abc" + +Output: ["a","aa","ab","aba","abb","abc"] + +Explanation: + +The sequence of key presses done by Alice are: + +Press key 1, and the string on the screen becomes "a". +Press key 1, and the string on the screen becomes "aa". +Press key 2, and the string on the screen becomes "ab". +Press key 1, and the string on the screen becomes "aba". +Press key 2, and the string on the screen becomes "abb". +Press key 2, and the string on the screen becomes "abc". +Example 2: + +Input: target = "he" + +Output: ["a","b","c","d","e","f","g","h","ha","hb","hc","hd","he"] + + + +Constraints: + +1 <= target.length <= 400 +target consists only of lowercase English letters. +""" +class Solution: + def stringSequence(self, target: str) -> List[str]: + """ + append + modify the last + greedy + """ + ret = [] + cur = [] + for c in target: + cur.append("a") + ret.append("".join(cur)) + while cur[-1] != c: + cur[-1] = chr(ord(cur[-1]) + 1) + ret.append("".join(cur)) + + return ret \ No newline at end of file diff --git a/3331 Find Subtree Sizes After Changes.py b/3331 Find Subtree Sizes After Changes.py new file mode 100644 index 0000000..8756098 --- /dev/null +++ b/3331 Find Subtree Sizes After Changes.py @@ -0,0 +1,147 @@ +#!/usr/bin/python3 +""" +You are given a tree rooted at node 0 that consists of n nodes numbered from 0 to n - 1. The tree is represented by an array parent of size n, where parent[i] is the parent of node i. Since node 0 is the root, parent[0] == -1. + +You are also given a string s of length n, where s[i] is the character assigned to node i. + +We make the following changes on the tree one time simultaneously for all nodes x from 1 to n - 1: + +Find the closest node y to node x such that y is an ancestor of x, and s[x] == s[y]. +If node y does not exist, do nothing. +Otherwise, remove the edge between x and its current parent and make node y the new parent of x by adding an edge between them. +Return an array answer of size n where answer[i] is the size of the +subtree + rooted at node i in the final tree. + + + +Example 1: + +Input: parent = [-1,0,0,1,1,1], s = "abaabc" + +Output: [6,3,1,1,1,1] + +Explanation: + + +The parent of node 3 will change from node 1 to node 0. + +Example 2: + +Input: parent = [-1,0,4,0,1], s = "abbba" + +Output: [5,2,1,1,1] + +Explanation: + + +The following changes will happen at the same time: + +The parent of node 4 will change from node 1 to node 0. +The parent of node 2 will change from node 4 to node 1. + + +Constraints: + +n == parent.length == s.length +1 <= n <= 10^5 +0 <= parent[i] <= n - 1 for all i >= 1. +parent[0] == -1 +parent represents a valid tree. +s consists only of lowercase English letters. +""" +from collections import defaultdict + + +class Solution: + def findSubtreeSizes(self, parent: List[int], s: str) -> List[int]: + """ + Naively dfs updating the tree results in TLE of O(N^2). + Need to do a topological dfs update on the tree for O(N). + * Does not require `visited` in topological sort since it's acyclic tree + * The `path` holds the map of a list of ancestors with a given char, with the last one as the closest + """ + G = defaultdict(list) + for i, pi in enumerate(parent): + G[pi].append(i) + + self.topo(G, 0, defaultdict(list), parent, s) + + G = defaultdict(list) + for i, pi in enumerate(parent): + G[pi].append(i) + + ret = [0 for _ in range(len(parent))] + self.dfs(G, 0, ret) + return ret + + def topo(self, G, cur, path, parent, s): + # topological dfs + char = s[cur] + if len(path[char]) > 0: + pi = path[char][~0] # or path[char][-1] + parent[cur] = pi + + path[char].append(cur) + for v in G[cur]: + self.topo(G, v, path, parent, s) + path[char].pop() + + def dfs(self, G, cur, ret): + # compute size + sz = 1 + for v in G[cur]: + sz += self.dfs(G, v, ret) + + ret[cur] = sz + return sz + + + +class SolutionTLE: + def findSubtreeSizes(self, parent: List[int], s: str) -> List[int]: + """ + Just do the operation + index 0 is always the root + + To get sub tree size, we can + 1. convert to TreeNode + 2. Sort pi array and then count. It does not work. It is not necessary pi > i + """ + new_parent = list(parent) # clone + for i in range(len(parent)): + pi = parent[i] # index + while pi != -1 and s[pi] != s[i]: + pi = parent[pi] + + if pi != -1: + new_parent[i] = pi + + return self.find_size(new_parent) + + def find_size(self, parent): + G = defaultdict(list) + for i, pi in enumerate(parent): + G[pi].append(i) + + ret = [0 for _ in parent] + self.dfs(G, 0, ret) + return ret + + def dfs(self, G, i, ret): + sz = 1 + for v in G[i]: + sz += self.dfs(G, v, ret) + + ret[i] = sz + return sz + + def find_size_wrong(self, parent): + # compute size + sz = [1 for _ in parent] + parent = [(pi, i) for i, pi in enumerate(parent)] + for pi, i in sorted(parent, reverse=True): + if pi != -1: + sz[pi] += sz[i] + + return sz diff --git a/3355 Zero Array Transformation I.py b/3355 Zero Array Transformation I.py new file mode 100644 index 0000000..78bbbf5 --- /dev/null +++ b/3355 Zero Array Transformation I.py @@ -0,0 +1,88 @@ +#!/usr/bin/python3 +""" +You are given an integer array nums of length n and a 2D array queries, where queries[i] = [li, ri]. + +For each queries[i]: + +Select a subset of indices within the range [li, ri] in nums. +Decrement the values at the selected indices by 1. +A Zero Array is an array where all elements are equal to 0. + +Return true if it is possible to transform nums into a Zero Array after processing all the queries sequentially, otherwise return false. + +A subset of an array is a selection of elements (possibly none) of the array. + + + +Example 1: + +Input: nums = [1,0,1], queries = [[0,2]] + +Output: true + +Explanation: + +For i = 0: +Select the subset of indices as [0, 2] and decrement the values at these indices by 1. +The array will become [0, 0, 0], which is a Zero Array. +Example 2: + +Input: nums = [4,3,2,1], queries = [[1,3],[0,2]] + +Output: false + +Explanation: + +For i = 0: +Select the subset of indices as [1, 2, 3] and decrement the values at these indices by 1. +The array will become [4, 2, 1, 0]. +For i = 1: +Select the subset of indices as [0, 1, 2] and decrement the values at these indices by 1. +The array will become [3, 1, 0, 0], which is not a Zero Array. + + +Constraints: + +1 <= nums.length <= 10^5 +0 <= nums[i] <= 10^5 +1 <= queries.length <= 10^5 +queries[i].length == 2 +0 <= li <= ri < nums.length +""" +class Solution: + def isZeroArray_TLE(self, nums: List[int], queries: List[List[int]]) -> bool: + """ + Naive solution: + Counter[i] represent times index i got hit by queries + Just check whether the counter is non-positive + We can reuse array as counter + + Time complexity: O(Q * N) + """ + for l, r in queries: + for i in range(l, r + 1): + nums[i] -= 1 + + for e in nums: + if e > 0: + return False + return True + + def isZeroArray(self, nums: List[int], queries: List[List[int]]) -> bool: + """ + prefix sum + sweep line algorithm + """ + n = len(nums) + pre = [0 for _ in range(n + 1)] + for l, r in queries: + pre[l] += 1 + pre[r+1] -= 1 + + # accumulate + for i in range(1, n + 1): + pre[i] += pre[i-1] + + for i in range(n): + if pre[i] < nums[i]: + return False + return True \ No newline at end of file diff --git a/3356 Zero Array Transformation II.py b/3356 Zero Array Transformation II.py new file mode 100644 index 0000000..d83c024 --- /dev/null +++ b/3356 Zero Array Transformation II.py @@ -0,0 +1,112 @@ +#!/usr/bin/python3 +""" +You are given an integer array nums of length n and a 2D array queries where queries[i] = [li, ri, vali]. + +Each queries[i] represents the following action on nums: + +Decrement the value at each index in the range [li, ri] in nums by at most vali. +The amount by which each value is decremented can be chosen independently for each index. +A Zero Array is an array with all its elements equal to 0. + +Return the minimum possible non-negative value of k, such that after processing the first k queries in sequence, nums becomes a Zero Array. If no such k exists, return -1. + + + +Example 1: + +Input: nums = [2,0,2], queries = [[0,2,1],[0,2,1],[1,1,3]] + +Output: 2 + +Explanation: + +For i = 0 (l = 0, r = 2, val = 1): +Decrement values at indices [0, 1, 2] by [1, 0, 1] respectively. +The array will become [1, 0, 1]. +For i = 1 (l = 0, r = 2, val = 1): +Decrement values at indices [0, 1, 2] by [1, 0, 1] respectively. +The array will become [0, 0, 0], which is a Zero Array. Therefore, the minimum value of k is 2. +Example 2: + +Input: nums = [4,3,2,1], queries = [[1,3,2],[0,2,1]] + +Output: -1 + +Explanation: + +For i = 0 (l = 1, r = 3, val = 2): +Decrement values at indices [1, 2, 3] by [2, 2, 1] respectively. +The array will become [4, 1, 0, 0]. +For i = 1 (l = 0, r = 2, val = 1): +Decrement values at indices [0, 1, 2] by [1, 1, 0] respectively. +The array will become [3, 0, 0, 0], which is not a Zero Array. + + +Constraints: + +1 <= nums.length <= 10^5 +0 <= nums[i] <= 5 * 10^5 +1 <= queries.length <= 10^5 +queries[i].length == 3 +0 <= li <= ri < nums.length +1 <= vali <= 5 +""" +class Solution: + def minZeroArray(self, nums: List[int], queries: List[List[int]]) -> int: + """ + online prefix sum + sweep line + optiized + """ + n = len(nums) + events = [0 for i in range(n+1)] + + accu = 0 + k = 0 + for i in range(n): + # process one num + while accu + events[i] < nums[i]: + # process one query + if k >= len(queries): + return -1 + + l, r, val = queries[k] + if i <= r: + events[max(l, i)] += val + events[r+1] -= val + + k += 1 + + # lazy increment at the end + accu += events[i] + + return k + + def minZeroArray_alternative(self, nums: List[int], queries: List[List[int]]) -> int: + """ + online prefix sum + sweep line + """ + n = len(nums) + events = [0 for i in range(n+1)] + + accu = 0 + k = 0 + for i in range(n): + # process one num + accu += events[i] # eager increment + while accu < nums[i] and k < len(queries): + # process one query + l, r, val = queries[k] + # future events + if l > i: + events[l] += val + if i <= r: + events[r+1] -= val + if l <= i and i <= r: + accu += val + + k += 1 + + if k >= len(queries) and accu < nums[i]: + return -1 + + return k From 793b97da9026064acede156e4ee534b936c17a61 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 28 Nov 2024 00:11:28 -0500 Subject: [PATCH 337/344] update --- 1143 Longest Common Subsequence.py | 51 +++++++ ... Distance After Road Addition Queries I.py | 130 ++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 1143 Longest Common Subsequence.py create mode 100644 3243 Shortest Distance After Road Addition Queries I.py diff --git a/1143 Longest Common Subsequence.py b/1143 Longest Common Subsequence.py new file mode 100644 index 0000000..d5bef10 --- /dev/null +++ b/1143 Longest Common Subsequence.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 +""" +Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0. + +A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters. + +For example, "ace" is a subsequence of "abcde". +A common subsequence of two strings is a subsequence that is common to both strings. + + + +Example 1: + +Input: text1 = "abcde", text2 = "ace" +Output: 3 +Explanation: The longest common subsequence is "ace" and its length is 3. +Example 2: + +Input: text1 = "abc", text2 = "abc" +Output: 3 +Explanation: The longest common subsequence is "abc" and its length is 3. +Example 3: + +Input: text1 = "abc", text2 = "def" +Output: 0 +Explanation: There is no such common subsequence, so the result is 0. + + +Constraints: + +1 <= text1.length, text2.length <= 1000 +text1 and text2 consist of only lowercase English characters. +""" +class Solution: + def longestCommonSubsequence(self, a: str, b: str) -> int: + """ + Let F_{i, j} be the longest common subsequence of a[:i] and b[:j] + """ + m, n = len(a), len(b) + F = [ + [0 for _ in range(n+1)] + for _ in range(m+1) + ] + for i in range(1, m+1): + for j in range(1, n+1): + if a[i-1] == b[j-1]: + F[i][j] = F[i-1][j-1] + 1 + else: + F[i][j] = max(F[i-1][j], F[i][j-1]) + + return F[m][n] diff --git a/3243 Shortest Distance After Road Addition Queries I.py b/3243 Shortest Distance After Road Addition Queries I.py new file mode 100644 index 0000000..042fc03 --- /dev/null +++ b/3243 Shortest Distance After Road Addition Queries I.py @@ -0,0 +1,130 @@ +#!/usr/bin/python3 +""" +You are given an integer n and a 2D integer array queries. + +There are n cities numbered from 0 to n - 1. Initially, there is a unidirectional road from city i to city i + 1 for all 0 <= i < n - 1. + +queries[i] = [ui, vi] represents the addition of a new unidirectional road from city ui to city vi. After each query, you need to find the length of the shortest path from city 0 to city n - 1. + +Return an array answer where for each i in the range [0, queries.length - 1], answer[i] is the length of the shortest path from city 0 to city n - 1 after processing the first i + 1 queries. + + + +Example 1: + +Input: n = 5, queries = [[2,4],[0,2],[0,4]] + +Output: [3,2,1] + +Explanation: + + + +After the addition of the road from 2 to 4, the length of the shortest path from 0 to 4 is 3. + + + +After the addition of the road from 0 to 2, the length of the shortest path from 0 to 4 is 2. + + + +After the addition of the road from 0 to 4, the length of the shortest path from 0 to 4 is 1. + +Example 2: + +Input: n = 4, queries = [[0,3],[0,2]] + +Output: [1,1] + +Explanation: + + + +After the addition of the road from 0 to 3, the length of the shortest path from 0 to 3 is 1. + + + +After the addition of the road from 0 to 2, the length of the shortest path remains 1. + + + +Constraints: + +3 <= n <= 500 +1 <= queries.length <= 500 +queries[i].length == 2 +0 <= queries[i][0] < queries[i][1] < n +1 < queries[i][1] - queries[i][0] +There are no repeated roads among the queries. +""" +from collections import defaultdict, deque + + +class Solution: + def shortestDistanceAfterQueries(self, n: int, queries: List[List[int]]) -> List[int]: + G = defaultdict(list) + dist = [] + for i in range(n-1): + G[i].append(i+1) + + for i in range(n): + dist.append(i) + + ret = [] + for u, v in queries: + G[u].append(v) + self.bfs(G, dist, u) + ret.append(dist[~0]) + + return ret + + def bfs(self, G, dist, s): + """ + * Known origin and end destination + * dist is the distance from origin, not to destination + * BFS update the distance from the source, where the source is the + start of the new edge, not the origin of the graph + """ + q = deque() + q.append(s) + while q: + v = q.popleft() + for nbr in G[v]: + if dist[nbr] > dist[v] + 1: + # It and its descendants require distance update + dist[nbr] = dist[v] + 1 + q.append(nbr) + + +class SolutionTLE: + def shortestDistanceAfterQueries(self, n: int, queries: List[List[int]]) -> List[int]: + """ + Naive solution: + 1. maintain a graph + 2. BFS + """ + G = defaultdict(list) + for i in range(n-1): + G[i].append(i+1) + + ret = [] + for q in queries: + s, t = q + G[s].append(t) + ret.append(self.bfs(G, 0, n - 1)) + + return ret + + def bfs(self, G, s, t): + q = [s] + ret = 0 + while q: + nxt_q = [] + ret += 1 + for v in q: + for nbr in G[v]: + if nbr == t: + return ret + nxt_q.append(nbr) + + q = nxt_q From f4d063e94b7a1f8d27106fa075d7ead4d6cb6785 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 10 Mar 2025 23:43:49 -0400 Subject: [PATCH 338/344] update --- 1079 Letter Tile Possibilities.py | 85 +++++++++ 1124 Longest Well-Performing Interval.py | 72 ++++++++ 1130 Minimum Cost Tree From Leaf Values.py | 108 ++++++++++++ ...solute Diff Less Than or Equal to Limit.py | 75 ++++++++ ...rices With a Special Discount in a Shop.py | 80 +++++++++ 1504 Count Submatrices With All Ones.py | 72 ++++++++ ...rray to be Removed to Make Array Sorted.py | 165 ++++++++++++++++++ 1673 Find the Most Competitive Subsequence.py | 56 ++++++ 1696 Jump Game VI.py | 87 +++++++++ ...exicographically Largest Valid Sequence.py | 78 +++++++++ 1856 Maximum Subarray Min-Product.py | 89 ++++++++++ ...e Number of Weak Characters in the Game.py | 54 ++++++ 2104 Sum of Subarray Ranges.py | 116 ++++++++++++ 2289 Steps to Make Array Non-decreasing.py | 108 ++++++++++++ ... Sum of a Pair With Equal Sum of Digits.py | 55 ++++++ 2762 Continuous Subarrays.py | 109 ++++++++++++ 2944 Minimum Number of Coins for Fruits.py | 117 +++++++++++++ 3092 Most Frequent IDs.py | 69 ++++++++ ...ke Binary Array Elements Equal to One I.py | 59 +++++++ ...e Binary Array Elements Equal to One II.py | 57 ++++++ ...nd the Minimum Area to Cover All Ones I.py | 77 ++++++++ ...atrices With Equal Frequency of X and Y.py | 160 +++++++++++++++++ 3271 Hash Divided String.py | 64 +++++++ 3282 Reach End of Array With Max Score.py | 68 ++++++++ 3286 Find a Safe Walk Through a Grid.py | 96 ++++++++++ 3290 Maximum Multiplication Score.py | 136 +++++++++++++++ ...ntaining Every Vowel and K Consonants I.py | 146 ++++++++++++++++ ...taining Every Vowel and K Consonants II.py | 93 ++++++++++ ...Possible Number by Binary Concatenation.py | 76 ++++++++ 3310 Remove Methods From Project.py | 93 ++++++++++ ...ind Maximum Removals From Source String.py | 138 +++++++++++++++ ...dentify the Largest Outlier in an Array.py | 72 ++++++++ ...f Target Nodes After Connecting Trees I.py | 88 ++++++++++ ...Subarray Sum With Length Divisible by K.py | 84 +++++++++ 3393 Count Paths With the Given XOR Value.py | 91 ++++++++++ ... Maximum Amount of Money Robot Can Earn.py | 133 ++++++++++++++ ...nimize the Maximum Edge Weight of Graph.py | 107 ++++++++++++ 3424 Minimum Cost to Make Arrays Identical.py | 66 +++++++ ...mum Sums of at Most Size K Subsequences.py | 97 ++++++++++ 3433 Count Mentions Per User.py | 130 ++++++++++++++ ...imum Frequency After Subarray Operation.py | 116 ++++++++++++ 3446 Sort Matrix by Diagonals.py | 97 ++++++++++ 3453 Separate Squares I.py | 110 ++++++++++++ 3457 Eat Pizzas.py | 98 +++++++++++ 853 Car Fleet.py | 77 ++++++++ 45 files changed, 4224 insertions(+) create mode 100644 1079 Letter Tile Possibilities.py create mode 100644 1124 Longest Well-Performing Interval.py create mode 100644 1130 Minimum Cost Tree From Leaf Values.py create mode 100644 1438 Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit.py create mode 100644 1475 Final Prices With a Special Discount in a Shop.py create mode 100644 1504 Count Submatrices With All Ones.py create mode 100644 1574 Shortest Subarray to be Removed to Make Array Sorted.py create mode 100644 1673 Find the Most Competitive Subsequence.py create mode 100644 1696 Jump Game VI.py create mode 100644 1718 Construct the Lexicographically Largest Valid Sequence.py create mode 100644 1856 Maximum Subarray Min-Product.py create mode 100644 1996 The Number of Weak Characters in the Game.py create mode 100644 2104 Sum of Subarray Ranges.py create mode 100644 2289 Steps to Make Array Non-decreasing.py create mode 100644 2342 Max Sum of a Pair With Equal Sum of Digits.py create mode 100644 2762 Continuous Subarrays.py create mode 100644 2944 Minimum Number of Coins for Fruits.py create mode 100644 3092 Most Frequent IDs.py create mode 100644 3191 Minimum Operations to Make Binary Array Elements Equal to One I.py create mode 100644 3192 Minimum Operations to Make Binary Array Elements Equal to One II.py create mode 100644 3195 Find the Minimum Area to Cover All Ones I.py create mode 100644 3212 Count Submatrices With Equal Frequency of X and Y.py create mode 100644 3271 Hash Divided String.py create mode 100644 3282 Reach End of Array With Max Score.py create mode 100644 3286 Find a Safe Walk Through a Grid.py create mode 100644 3290 Maximum Multiplication Score.py create mode 100644 3305 Count of Substrings Containing Every Vowel and K Consonants I.py create mode 100644 3306 Count of Substrings Containing Every Vowel and K Consonants II.py create mode 100644 3309 Maximum Possible Number by Binary Concatenation.py create mode 100644 3310 Remove Methods From Project.py create mode 100644 3316 Find Maximum Removals From Source String.py create mode 100644 3371 Identify the Largest Outlier in an Array.py create mode 100644 3372 Maximize the Number of Target Nodes After Connecting Trees I.py create mode 100644 3381 Maximum Subarray Sum With Length Divisible by K.py create mode 100644 3393 Count Paths With the Given XOR Value.py create mode 100644 3418 Maximum Amount of Money Robot Can Earn.py create mode 100644 3419 Minimize the Maximum Edge Weight of Graph.py create mode 100644 3424 Minimum Cost to Make Arrays Identical.py create mode 100644 3428 Maximum and Minimum Sums of at Most Size K Subsequences.py create mode 100644 3433 Count Mentions Per User.py create mode 100644 3434 Maximum Frequency After Subarray Operation.py create mode 100644 3446 Sort Matrix by Diagonals.py create mode 100644 3453 Separate Squares I.py create mode 100644 3457 Eat Pizzas.py create mode 100644 853 Car Fleet.py diff --git a/1079 Letter Tile Possibilities.py b/1079 Letter Tile Possibilities.py new file mode 100644 index 0000000..0734880 --- /dev/null +++ b/1079 Letter Tile Possibilities.py @@ -0,0 +1,85 @@ +""" +You have n tiles, where each tile has one letter tiles[i] printed on it. + +Return the number of possible non-empty sequences of letters you can make using the letters printed on those tiles. + + + +Example 1: + +Input: tiles = "AAB" +Output: 8 +Explanation: The possible sequences are "A", "B", "AA", "AB", "BA", "AAB", "ABA", "BAA". +Example 2: + +Input: tiles = "AAABBC" +Output: 188 +Example 3: + +Input: tiles = "V" +Output: 1 + + +Constraints: + +1 <= tiles.length <= 7 +tiles consists of uppercase English letters. +""" +import math +from collections import defaultdict + + +class Solution: + def numTilePossibilities(self, tiles: str) -> int: + """ + brute force backtracking + combinatorics + """ + cnt = 0 + ret = set() + self.backtrack(tiles, 0, [], ret) + for s in ret: + if s == "": + continue + cur = math.factorial(len(s)) + counter = defaultdict(int) + for c in s: + counter[c] += 1 + for v in counter.values(): + cur //= math.factorial(v) + cnt += cur + + return cnt + + def backtrack(self, tiles, i, cur, ret): + if i == len(tiles): + ret.add("".join(sorted(cur))) + return + + self.backtrack(tiles, i+1, cur, ret) + + cur.append(tiles[i]) + self.backtrack(tiles, i+1, cur, ret) + cur.pop() + + +class SolutionBruteForce: + def numTilePossibilities(self, tiles: str) -> int: + """ + DFS + all tile are interconnected as neighbors + """ + visited = [False for _ in tiles] + ret = set() + self.dfs(tiles, visited, [], ret) + return len(ret) - 1 # exclude "" + + def dfs(self, tiles, visited, cur, ret): + ret.add("".join(cur)) + + for i, v in enumerate(tiles): + if not visited[i]: + visited[i] = True + cur.append(v) + self.dfs(tiles, visited, cur, ret) + cur.pop() + visited[i] = False \ No newline at end of file diff --git a/1124 Longest Well-Performing Interval.py b/1124 Longest Well-Performing Interval.py new file mode 100644 index 0000000..d70054a --- /dev/null +++ b/1124 Longest Well-Performing Interval.py @@ -0,0 +1,72 @@ +""" +We are given hours, a list of the number of hours worked per day for a given employee. + +A day is considered to be a tiring day if and only if the number of hours worked is (strictly) greater than 8. + +A well-performing interval is an interval of days for which the number of tiring days is strictly larger than the number of non-tiring days. + +Return the length of the longest well-performing interval. + + + +Example 1: + +Input: hours = [9,9,6,0,6,6,9] +Output: 3 +Explanation: The longest well-performing interval is [9,9,6]. +Example 2: + +Input: hours = [6,6,6] +Output: 0 + + +Constraints: + +1 <= hours.length <= 10^4 +0 <= hours[i] <= 16 +""" +class Solution: + def longestWPI_TLE(self, hours: List[int]) -> int: + """ + Use a stack to hold tiring day? + + Not necessarily + + use prefix sum + """ + A = [1 if hour > 8 else -1 for hour in hours] + N = len(A) + prefix = [0 for _ in range(N+1)] # A[:i] + for i in range(1, N+1): + prefix[i] = prefix[i-1] + A[i-1] + + ret = 0 + for i in range(N+1): # not from 1 + for j in range(i+1, N+1): + if prefix[j] - prefix[i] > 0: + ret = max(ret, j - i) + + return ret + + def longestWPI(self, hours: List[int]) -> int: + """ + use prefix sum + monotonic stack + """ + A = [1 if hour > 8 else -1 for hour in hours] + N = len(A) + prefix = [0 for _ in range(N+1)] # A[:i] + for i in range(1, N+1): + prefix[i] = prefix[i-1] + A[i-1] + + stk = [] # monotonic decreasing stack with increasing index + for i in range(N+1): + if not stk or prefix[stk[~0]] > prefix[i]: + stk.append(i) + + ret = 0 + for j in range(N, 0, -1): + while stk and prefix[j] - prefix[stk[~0]] > 0: + i = stk.pop() + ret = max(ret, j - i) + + return ret \ No newline at end of file diff --git a/1130 Minimum Cost Tree From Leaf Values.py b/1130 Minimum Cost Tree From Leaf Values.py new file mode 100644 index 0000000..37edefb --- /dev/null +++ b/1130 Minimum Cost Tree From Leaf Values.py @@ -0,0 +1,108 @@ +""" +Given an array arr of positive integers, consider all binary trees such that: + +Each node has either 0 or 2 children; +The values of arr correspond to the values of each leaf in an in-order traversal of the tree. +The value of each non-leaf node is equal to the product of the largest leaf value in its left and right subtree, respectively. +Among all possible binary trees considered, return the smallest possible sum of the values of each non-leaf node. It is guaranteed this sum fits into a 32-bit integer. + +A node is a leaf if and only if it has zero children. + + + +Example 1: + + +Input: arr = [6,2,4] +Output: 32 +Explanation: There are two possible trees shown. +The first has a non-leaf node sum 36, and the second has non-leaf node sum 32. +Example 2: + + +Input: arr = [4,11] +Output: 44 + + +Constraints: + +2 <= arr.length <= 40 +1 <= arr[i] <= 15 +It is guaranteed that the answer fits into a 32-bit signed integer (i.e., it is less than 2^31). +""" +import functools + + +class SolutionDP: + def mctFromLeafValues(self, A: List[int]) -> int: + """ + The value of each non-leaf node is equal to the product of the largest leaf value in its left and right subtree, respectively. + We cannot sort A + We need to combine neighbor into 1 operation + + Smallest non-leaf sum + + Brute force? + + [6, 2, 4] + + Let F_{i, j} be the smallest in A[i:j] + + Then partion F_{i, j} + F_{i, j} = F_{i, k} + F{k, j} + max(A[i:k])*max(A[k:j]) + min over k \in [i, j) + + How to build this DP? memoization + """ + return self.F(0, len(A), tuple(A)) + + @functools.lru_cache(maxsize=None) + def F(self, i, j, A): + return min( + ( + self.F(i, k, A) + self.F(k, j, A) + + max(A[i:k]) * max(A[k:j]) + for k in range(i+1, j) + ), + default=0, + ) + + +class Solution: + def mctFromLeafValues(self, A: List[int]) -> int: + """ + Max leaf is used at each inner node => put big leaf nodes close to the root. + => greedily start with the smallest leaf + + To remove any number a, it costs a * b, where b >= a. + So `a` has to be removed by a bigger neighbor `b`. + To minimize this cost, we need to minimize b. + + i = A.index(min(A)) + cost += A[i] * min(A[i-1], A[i+1]) + A.pop(i) + O(N^2) + + How to efficiently find the min index + We need to find i s.t. A[i-1] > A[i] < A[i+1] + Keep a monotonic stack for A[:i], till the monotonicity break by A[i], then we need to pop and maintain the monotonicity + """ + cost = 0 + stk = [] + for a in A: + while stk and stk[-1] <= a: + mid = stk.pop() + if stk: + left = stk[-1] + cost += mid * min(left, a) + else: + cost += mid * a + + stk.append(a) + + while len(stk) > 1: + mid = stk.pop() + left = stk[-1] + cost += mid * left + + return cost diff --git a/1438 Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit.py b/1438 Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit.py new file mode 100644 index 0000000..1da5c38 --- /dev/null +++ b/1438 Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit.py @@ -0,0 +1,75 @@ +""" +Given an array of integers nums and an integer limit, return the size of the longest non-empty subarray such that the absolute difference between any two elements of this subarray is less than or equal to limit. + +Example 1: + +Input: nums = [8,2,4,7], limit = 4 +Output: 2 +Explanation: All subarrays are: +[8] with maximum absolute diff |8-8| = 0 <= 4. +[8,2] with maximum absolute diff |8-2| = 6 > 4. +[8,2,4] with maximum absolute diff |8-2| = 6 > 4. +[8,2,4,7] with maximum absolute diff |8-2| = 6 > 4. +[2] with maximum absolute diff |2-2| = 0 <= 4. +[2,4] with maximum absolute diff |2-4| = 2 <= 4. +[2,4,7] with maximum absolute diff |2-7| = 5 > 4. +[4] with maximum absolute diff |4-4| = 0 <= 4. +[4,7] with maximum absolute diff |4-7| = 3 <= 4. +[7] with maximum absolute diff |7-7| = 0 <= 4. +Therefore, the size of the longest subarray is 2. +Example 2: + +Input: nums = [10,1,2,4,7,2], limit = 5 +Output: 4 +Explanation: The subarray [2,4,7,2] is the longest since the maximum absolute diff is |2-7| = 5 <= 5. +Example 3: + +Input: nums = [4,2,2,2,4,4,2,2], limit = 0 +Output: 3 + + +Constraints: + +1 <= nums.length <= 10^5 +1 <= nums[i] <= 10^9 +0 <= limit <= 10^9 +""" +from collections import deque + + +class Solution: + def longestSubarray(self, A: List[int], limit: int) -> int: + """ + Keep track the max in a sliding window -> use a monotonic queue + * when iterating j, q represent the max indicies in the window ending at j + + monotonic queue + """ + q_max = deque() # q_max[0] represent the current max, monotonically decreasing + q_min = deque() # q_min[0] represent the current min, monotonically increasing + i = 0 # left + j = 0 # right + ret = 0 + + while j < len(A): + # process A[j] + while q_max and A[q_max[~0]] <= A[j]: + q_max.pop() + q_max.append(j) + while q_min and A[q_min[~0]] >= A[j]: + q_min.pop() + q_min.append(j) + + while q_max and q_min and A[q_max[0]] - A[q_min[0]] > limit: + # q need to store indices + # move to the smaller index + if q_max[0] < q_min[0]: + i = q_max.popleft() + 1 + else: + i = q_min.popleft() + 1 + + l = j - i + 1 + ret = max(ret, l) + j += 1 + + return ret \ No newline at end of file diff --git a/1475 Final Prices With a Special Discount in a Shop.py b/1475 Final Prices With a Special Discount in a Shop.py new file mode 100644 index 0000000..a10a835 --- /dev/null +++ b/1475 Final Prices With a Special Discount in a Shop.py @@ -0,0 +1,80 @@ +""" +You are given an integer array prices where prices[i] is the price of the ith item in a shop. + +There is a special discount for items in the shop. If you buy the ith item, then you will receive a discount equivalent to prices[j] where j is the minimum index such that j > i and prices[j] <= prices[i]. Otherwise, you will not receive any discount at all. + +Return an integer array answer where answer[i] is the final price you will pay for the ith item of the shop, considering the special discount. + + + +Example 1: + +Input: prices = [8,4,6,2,3] +Output: [4,2,4,2,3] +Explanation: +For item 0 with price[0]=8 you will receive a discount equivalent to prices[1]=4, therefore, the final price you will pay is 8 - 4 = 4. +For item 1 with price[1]=4 you will receive a discount equivalent to prices[3]=2, therefore, the final price you will pay is 4 - 2 = 2. +For item 2 with price[2]=6 you will receive a discount equivalent to prices[3]=2, therefore, the final price you will pay is 6 - 2 = 4. +For items 3 and 4 you will not receive any discount at all. +Example 2: + +Input: prices = [1,2,3,4,5] +Output: [1,2,3,4,5] +Explanation: In this case, for all items, you will not receive any discount at all. +Example 3: + +Input: prices = [10,1,1,6] +Output: [9,0,1,6] + + +Constraints: + +1 <= prices.length <= 500 +1 <= prices[i] <= 1000 +""" +class Solution: + def finalPrices_error(self, A: List[int]) -> List[int]: + """ + Brute force O(N^2) + + Maintain a stack from the right, keep the earliest minmum value at the top of the stk + Stk is monotonic decreasing + """ + R = [] + N = len(A) + stk = [] + for i in range(N-1, -1, -1): + if not stk or A[stk[-1]] > A[i]: + stk.append(i) + + for i in range(N): + while stk and stk[-1] <= i: + stk.pop() + price = A[i] + if stk and A[stk[-1]] < A[i]: + price -= A[stk[-1]] + R.append(price) + + return R + + def finalPrices(self, A: List[int]) -> List[int]: + """ + Brute force O(N^2) + + Find the next closest smaller element + -> Find the previous closest larger element + """ + N = len(A) + R = [0 for _ in range(N)] + stk = [] # store the previous, monotonic increasing + for i in range(N): + while stk and A[stk[-1]] >= A[i]: + prev = stk.pop() + R[prev] = A[prev] - A[i] + stk.append(i) + + for i in stk: + R[i] = A[i] + + return R + diff --git a/1504 Count Submatrices With All Ones.py b/1504 Count Submatrices With All Ones.py new file mode 100644 index 0000000..60b5c9c --- /dev/null +++ b/1504 Count Submatrices With All Ones.py @@ -0,0 +1,72 @@ +""" +Given an m x n binary matrix mat, return the number of submatrices that have all ones. + +Example 1: + +Input: mat = [ + [1,0,1], + [1,1,0], + [1,1,0] +] +Output: 13 +Explanation: +There are 6 rectangles of side 1x1. +There are 2 rectangles of side 1x2. +There are 3 rectangles of side 2x1. +There is 1 rectangle of side 2x2. +There is 1 rectangle of side 3x1. +Total number of rectangles = 6 + 2 + 3 + 1 + 1 = 13. +Example 2: + + +Input: mat = [[0,1,1,0],[0,1,1,1],[1,1,1,0]] +Output: 24 +Explanation: +There are 8 rectangles of side 1x1. +There are 5 rectangles of side 1x2. +There are 2 rectangles of side 1x3. +There are 4 rectangles of side 2x1. +There are 2 rectangles of side 2x2. +There are 2 rectangles of side 3x1. +There is 1 rectangle of side 3x2. +Total number of rectangles = 8 + 5 + 2 + 4 + 2 + 2 + 1 = 24. + + +Constraints: + +1 <= m, n <= 150 +mat[i][j] is either 0 or 1. +""" +class Solution: + def numSubmat(self, mat: List[List[int]]) -> int: + """ + 1-D 1-submatrix: + [1, 0, 1, 1, 1] + Let F[j] be number of 1-submatrix ending AT j-1 + + F[j] = F[j] + 1 if F[j-1] == 1 + 0 otherwise + In the end, sum(F) + + 2-D submatrix: + [1, 0, 1, 1, 1] + [1, 1, 0, 1, 1] + Consider a 2-row matrix, Project 2D into 1D: + [1, 0, 0, 1, 1] + """ + ret = 0 + M = len(mat) + N = len(mat[0]) + for lo in range(M): + # is all ones for mat[lo:hi][j] + is_ones_col = [True for j in range(N)] + for hi in range(lo+1, M+1): + for j in range(N): + is_ones_col[j] &= mat[hi-1][j] # mem saving + + F = [0 for _ in range(N+1)] + for j in range(1, N+1): + F[j] = F[j-1] + 1 if is_ones_col[j-1] else 0 + ret += F[j] + + return ret \ No newline at end of file diff --git a/1574 Shortest Subarray to be Removed to Make Array Sorted.py b/1574 Shortest Subarray to be Removed to Make Array Sorted.py new file mode 100644 index 0000000..da309dc --- /dev/null +++ b/1574 Shortest Subarray to be Removed to Make Array Sorted.py @@ -0,0 +1,165 @@ +""" +Given an integer array arr, remove a subarray (can be empty) from arr such that the remaining elements in arr are non-decreasing. + +Return the length of the shortest subarray to remove. + +A subarray is a contiguous subsequence of the array. + + + +Example 1: + +Input: arr = [1,2,3,10,4,2,3,5] +Output: 3 +Explanation: The shortest subarray we can remove is [10,4,2] of length 3. The remaining elements after that will be [1,2,3,3,5] which are sorted. +Another correct solution is to remove the subarray [3,10,4]. +Example 2: + +Input: arr = [5,4,3,2,1] +Output: 4 +Explanation: Since the array is strictly decreasing, we can only keep a single element. Therefore we need to remove a subarray of length 4, either [5,4,3,2] or [4,3,2,1]. +Example 3: + +Input: arr = [1,2,3] +Output: 0 +Explanation: The array is already non-decreasing. We do not need to remove any elements. + + +Constraints: + +1 <= arr.length <= 10^5 +0 <= arr[i] <= 10^9 +""" +import bisect + + +class SolutionAdditionalMemory: + def findLengthOfShortestSubarray_suboptimal(self, A: List[int]) -> int: + """ + It is subarray, not subsequence. For subsequence, may use DP + + Has to be sorted + [1,2,3,10,4,2,3,5] + option1: [1,2,3,10] + option2: [1,2,3,4,5] + + next larger element? + 0 1 2 3 4 5 6 7 8 + [1,2,3,10,4,2,3,5] + [2,3,10,\0,5,3,5,\0] + [1,2,3,8, 8,6,7,8] + + (val, idx) + does not work , since (3, 2) need to consider (2, 5) + + Complementary: + find the longest increasing subarray prefix + find the longest increasing subarray suffix + + then keep popping either stack to make a single subarray increasing + prefix: [1, 2, 3, 10] + suffix: [5, 3, 2] + O(N^2) to search the stack + or bisect O(N logN) + """ + prefix = [] + for a in A: + if not prefix or prefix[-1] <= a: + prefix.append(a) + else: + break + suffix = [] + for a in A[::-1]: + if not suffix or suffix[-1] >= a: + suffix.append(a) + else: + break + + maxa = 0 + p = list(prefix) + # bisect only works on asc array + suffix_rev = suffix[::-1] + while p: + t = p[-1] + idx = bisect.bisect_right(suffix_rev, t) + maxa = max(maxa, len(p) + len(suffix_rev) - idx) + p.pop() + s = list(suffix) + while s: + t = s[-1] + idx = bisect.bisect_right(prefix, t) + maxa = max(maxa, len(s) + idx) + s.pop() + + return max(0, len(A) - maxa) # handle double counting of the same element + + def findLengthOfShortestSubarray(self, A: List[int]) -> int: + """ + prefix: [1, 2, 3, 10] + suffix: [5, 3, 2] + + Bridge the two sorted arrays can be O(N) + """ + prefix = [] + for a in A: + if not prefix or prefix[-1] <= a: + prefix.append(a) + else: + break + suffix = [] + for a in A[::-1]: + if not suffix or suffix[-1] >= a: + suffix.append(a) + else: + break + + maxa = max(len(suffix), len(prefix)) + j = len(suffix) - 1 + for i in range(len(prefix)): + # this does not handle empty prefix + while j >= 0 and suffix[j] < prefix[i]: + j -= 1 + maxa = max(maxa, i+1 + j+1) + + return max(0, len(A) - maxa) # handle double counting of the same element + + +class Solution: + def findLengthOfShortestSubarray(self, A: List[int]) -> int: + """ + Use pointers instead of additional memory + + Bridget two asc sorted list + [1, 2, 3, 4, 5] + [3, 4, 5] + """ + N = len(A) + + # sorted: A[:prefix] + prefix = 0 + for i in range(N): + if prefix == 0 or A[i] >= A[prefix-1]: + prefix = i + 1 + else: + break + + # sorted: A[suffix:] + suffix = N + for i in range(N-1, -1, -1): + if suffix == N or A[i] <= A[suffix]: + suffix = i + else: + break + + if prefix > suffix: + return 0 + + maxa = 0 + j = suffix + for i in range(prefix+1): + while i-1 >= 0 and j < N and A[i-1] > A[j]: + j += 1 + + maxa = max(maxa, i+(N-j)) + + return N - maxa \ No newline at end of file diff --git a/1673 Find the Most Competitive Subsequence.py b/1673 Find the Most Competitive Subsequence.py new file mode 100644 index 0000000..45c3915 --- /dev/null +++ b/1673 Find the Most Competitive Subsequence.py @@ -0,0 +1,56 @@ +""" +Given an integer array nums and a positive integer k, return the most competitive subsequence of nums of size k. + +An array's subsequence is a resulting sequence obtained by erasing some (possibly zero) elements from the array. + +We define that a subsequence a is more competitive than a subsequence b (of the same length) if in the first position where a and b differ, subsequence a has a number less than the corresponding number in b. For example, [1,3,4] is more competitive than [1,3,5] because the first position they differ is at the final number, and 4 is less than 5. + + + +Example 1: + +Input: nums = [3,5,2,6], k = 2 +Output: [2,6] +Explanation: Among the set of every possible subsequence: {[3,5], [3,2], [3,6], [5,2], [5,6], [2,6]}, [2,6] is the most competitive. +Example 2: + +Input: nums = [2,4,3,3,5,4,9,6], k = 4 +Output: [2,3,3,4] + + +Constraints: + +1 <= nums.length <= 10^5 +0 <= nums[i] <= 10^9 +1 <= k <= nums.length +""" +class Solution: + def mostCompetitive(self, A: List[int], k: int) -> List[int]: + """ + sort and get the min? + subsequence still maintain the order + + find the next smaller number of each element + build the k-size subsequence from next smaller element + then take the min + O(NK) + + Can we reduce time compexlity? + short curcit when reach A[N_K:K] + sort, start from smallest O(N logN) + + Can we do better? + Monotonic stack in one pass + Different than finding the next smaller number + """ + # monotonically increasing stk as the result + stk = [] + N = len(A) + for i in range(N): + # remaining >= required + while stk and A[stk[-1]] > A[i] and N - i >= k - (len(stk) - 1): + stk.pop() + stk.append(i) + + ret = [A[stk[i]] for i in range(k)] + return ret \ No newline at end of file diff --git a/1696 Jump Game VI.py b/1696 Jump Game VI.py new file mode 100644 index 0000000..583679b --- /dev/null +++ b/1696 Jump Game VI.py @@ -0,0 +1,87 @@ +""" +You are given a 0-indexed integer array nums and an integer k. + +You are initially standing at index 0. In one move, you can jump at most k steps forward without going outside the boundaries of the array. That is, you can jump from index i to any index in the range [i + 1, min(n - 1, i + k)] inclusive. + +You want to reach the last index of the array (index n - 1). Your score is the sum of all nums[j] for each index j you visited in the array. + +Return the maximum score you can get. + + + +Example 1: + +Input: nums = [1,-1,-2,4,-7,3], k = 2 +Output: 7 +Explanation: You can choose your jumps forming the subsequence [1,-1,4,3] (underlined above). The sum is 7. +Example 2: + +Input: nums = [10,-5,-2,4,0,3], k = 3 +Output: 17 +Explanation: You can choose your jumps forming the subsequence [10,4,3] (underlined above). The sum is 17. +Example 3: + +Input: nums = [1,-5,-20,4,-1,3,-6,-3], k = 2 +Output: 0 + + +Constraints: + +1 <= nums.length, k <= 10^5 +-104 <= nums[i] <= 10^4 +""" +import sys +from collections import deque + + +class Solution: + def maxResultTLE(self, A: List[int], k: int) -> int: + """ + DP + Let F[i] be the maximum at A[i] + F[i] = A[i] + max( + F[i-1] + F[i-2] + ... + F[i-k] + ) + """ + N = len(A) + F = [-sys.maxsize-1 for _ in range(N)] + F[0] = A[0] + for i in range(1, N): + for j in range(i-1, max(0, i-k) -1, -1): + F[i] = max(F[i], F[j]) + F[i] += A[i] + + return F[~0] + + def maxResult(self, A: List[int], k: int) -> int: + """ + DP + Let F[i] be the maximum at A[i] + F[i] = A[i] + max( + F[i-1] + F[i-2] + ... + F[i-k] + ) + use monotonic queue to keep track the max in window F[i-k:i] + """ + N = len(A) + F = [-sys.maxsize-1 for _ in range(N)] + F[0] = A[0] + q = deque() # max, monotonic decreasing to keep track 1st, 2nd, 3rd largest + for i in range(1, N): + # processing F[i-1] + while q and F[q[~0]] <= F[i-1]: + q.pop() + q.append(i-1) + + # indices falling out of window + while q and q[0] < i - k: + q.popleft() + + F[i] = A[i] + F[q[0]] + + return F[~0] \ No newline at end of file diff --git a/1718 Construct the Lexicographically Largest Valid Sequence.py b/1718 Construct the Lexicographically Largest Valid Sequence.py new file mode 100644 index 0000000..7b3e6ff --- /dev/null +++ b/1718 Construct the Lexicographically Largest Valid Sequence.py @@ -0,0 +1,78 @@ +""" +""" +class SolutionTLE: + def constructDistancedSequence(self, n: int) -> List[int]: + """ + backtrack enumeration + + brute-force + * iterate through numbers + * iterate through position + """ + maxa = [-1 for _ in range(2*n-1)] + self.backtrack(list(maxa), n, 1, maxa) + return maxa + + def backtrack(self, A, n, x, maxa): + if all(a != -1 for a in A): + maxa[:] = max(maxa, A) # modify in place + return + + for j in range(len(A)): + if A[j] == -1: + if x == 1: + A[j] = x + self.backtrack(A, n, x+1, maxa) + A[j] = -1 + elif j + x < len(A) and A[j+x] == -1: + A[j] = x + A[j+x] = x + self.backtrack(A, n, x+1, maxa) + A[j] = -1 + A[j+x] = -1 + + +class Solution: + def constructDistancedSequence(self, n: int) -> List[int]: + """ + backtrack enumeration + + brute-force + * iterate through position first + * iterate through numbers + """ + maxa = [-1 for _ in range(2*n-1)] + visited = [False for _ in range(n+1)] + self.backtrack(list(maxa), 0, n, visited, maxa) + return maxa + + def backtrack(self, A, i, n, visited, maxa): + if all(a != -1 for a in A): + maxa[:] = max(maxa, A) # modify in place + return True + + for x in range(n, 0, -1): + if not visited[x]: + if A[i] == -1: + if x == 1: + A[i] = x + visited[x] = True + if self.backtrack(A, i+1, n, visited, maxa): + return True + A[i] = -1 + visited[x] = False + elif i+x < len(A) and A[i+x] == -1: + A[i] = x + A[i+x] = x + visited[x] = True + if self.backtrack(A, i+1, n, visited, maxa): + return True + A[i] = -1 + A[i+x] = -1 + visited[x] = False + else: + return self.backtrack(A, i+1, n, visited, maxa) + + return False + + \ No newline at end of file diff --git a/1856 Maximum Subarray Min-Product.py b/1856 Maximum Subarray Min-Product.py new file mode 100644 index 0000000..d05d481 --- /dev/null +++ b/1856 Maximum Subarray Min-Product.py @@ -0,0 +1,89 @@ +""" +The min-product of an array is equal to the minimum value in the array multiplied by the array's sum. + +For example, the array [3,2,5] (minimum value is 2) has a min-product of 2 * (3+2+5) = 2 * 10 = 20. +Given an array of integers nums, return the maximum min-product of any non-empty subarray of nums. Since the answer may be large, return it modulo 10^9 + 7. + +Note that the min-product should be maximized before performing the modulo operation. Testcases are generated such that the maximum min-product without modulo will fit in a 64-bit signed integer. + +A subarray is a contiguous part of an array. + + + +Example 1: + +Input: nums = [1,2,3,2] +Output: 14 +Explanation: The maximum min-product is achieved with the subarray [2,3,2] (minimum value is 2). +2 * (2+3+2) = 2 * 7 = 14. +Example 2: + +Input: nums = [2,3,3,1,2] +Output: 18 +Explanation: The maximum min-product is achieved with the subarray [3,3] (minimum value is 3). +3 * (3+3) = 3 * 6 = 18. +Example 3: + +Input: nums = [3,1,5,6,4,2] +Output: 60 +Explanation: The maximum min-product is achieved with the subarray [5,6,4] (minimum value is 4). +4 * (5+6+4) = 4 * 15 = 60. + + +Constraints: + +1 <= nums.length <= 10^5 +1 <= nums[i] <= 10^7 +""" +import sys + + +MOD = int(1e9+7) + + +class Solution: + def maxSumMinProduct(self, A: List[int]) -> int: + """ + min(A[i:j]) * sum(A[i:j]) + shrink i, j will make min larger while sum smaller + sum can be get in O(1) by prefix sum + min(A[i:j]) is O(1) by keeping track of min when iterating j + O(N^2) + + Can we do in O(N) by some two pointers? + + Can we find the maximum min-product for every value in the array? + For every value, find the next smaller on the right, and similarly on the left + """ + N = len(A) + P = [0 for _ in range(N+1)] # prefix sum of A[:i] + for i in range(1, N+1): + P[i] = P[i-1] + A[i-1] + + R = [N for _ in range(N)] # next smaller on right + # monotonic increasing stack, storing pending candidate that have not found the next smaller element, + # til monotonicity break, pop and process it + stk = [] + for i in range(N): + while stk and A[stk[~0]] > A[i]: + t = stk.pop() + R[t] = i + stk.append(i) + + L = [-1 for _ in range(N)] # next smaller on the left + for i in range(N-1, -1, -1): + while stk and A[stk[~0]] > A[i]: + t = stk.pop() + L[t] = i + stk.append(i) + + maxa = -sys.maxsize-1 + for i in range(N): + l = L[i] + r = R[i] + # sum(A[l+1:r]) + s = P[r] - P[l+1] + maxa = max(maxa, s * A[i]) + + return maxa % MOD + \ No newline at end of file diff --git a/1996 The Number of Weak Characters in the Game.py b/1996 The Number of Weak Characters in the Game.py new file mode 100644 index 0000000..cc573c6 --- /dev/null +++ b/1996 The Number of Weak Characters in the Game.py @@ -0,0 +1,54 @@ +""" +You are playing a game that contains multiple characters, and each of the characters has two main properties: attack and defense. You are given a 2D integer array properties where properties[i] = [attacki, defensei] represents the properties of the ith character in the game. + +A character is said to be weak if any other character has both attack and defense levels strictly greater than this character's attack and defense levels. More formally, a character i is said to be weak if there exists another character j where attackj > attacki and defensej > defensei. + +Return the number of weak characters. + + + +Example 1: + +Input: properties = [[5,5],[6,3],[3,6]] +Output: 0 +Explanation: No character has strictly greater attack and defense than the other. +Example 2: + +Input: properties = [[2,2],[3,3]] +Output: 1 +Explanation: The first character is weak because the second character has a strictly greater attack and defense. +Example 3: + +Input: properties = [[1,5],[10,4],[4,3]] +Output: 1 +Explanation: The third character is weak because the second character has a strictly greater attack and defense. + + +Constraints: + +2 <= properties.length <= 10^5 +properties[i].length == 2 +1 <= attacki, defensei <= 10^5 +""" +class Solution: + def numberOfWeakCharacters(self, A: List[List[int]]) -> int: + """ + If it is 1D, it is just to find the local min, not global (\exists vs. \forall) + If it is 1D, just find the max, anything under max is weak + + Reduce 2D to 1D? + Points on the pareto effiency curve, anything below is weak + Sort by x-axis, then check y-axis? + Keep a stack of monotonically decreasing y-axis + """ + stk = [] # y monotonically decreasing + A.sort(key=lambda x: (x[0], -x[1])) # sort x asc and then y desc + ret = 0 + for i, v in enumerate(A): + x, y = v + while stk and A[stk[-1]][0] < x and A[stk[-1]][1] < y: + stk.pop() + ret += 1 + stk.append(i) + + return ret \ No newline at end of file diff --git a/2104 Sum of Subarray Ranges.py b/2104 Sum of Subarray Ranges.py new file mode 100644 index 0000000..e2b469f --- /dev/null +++ b/2104 Sum of Subarray Ranges.py @@ -0,0 +1,116 @@ +""" +You are given an integer array nums. The range of a subarray of nums is the difference between the largest and smallest element in the subarray. + +Return the sum of all subarray ranges of nums. + +A subarray is a contiguous non-empty sequence of elements within an array. + + + +Example 1: + +Input: nums = [1,2,3] +Output: 4 +Explanation: The 6 subarrays of nums are the following: +[1], range = largest - smallest = 1 - 1 = 0 +[2], range = 2 - 2 = 0 +[3], range = 3 - 3 = 0 +[1,2], range = 2 - 1 = 1 +[2,3], range = 3 - 2 = 1 +[1,2,3], range = 3 - 1 = 2 +So the sum of all ranges is 0 + 0 + 0 + 1 + 1 + 2 = 4. +Example 2: + +Input: nums = [1,3,3] +Output: 4 +Explanation: The 6 subarrays of nums are the following: +[1], range = largest - smallest = 1 - 1 = 0 +[3], range = 3 - 3 = 0 +[3], range = 3 - 3 = 0 +[1,3], range = 3 - 1 = 2 +[3,3], range = 3 - 3 = 0 +[1,3,3], range = 3 - 1 = 2 +So the sum of all ranges is 0 + 0 + 0 + 2 + 0 + 2 = 4. +Example 3: + +Input: nums = [4,-2,-3,4,1] +Output: 59 +Explanation: The sum of all subarray ranges of nums is 59. + + +Constraints: + +1 <= nums.length <= 1000 +-109 <= nums[i] <= 10^9 + + +Follow-up: Could you find a solution with O(n) time complexity? +""" +import sys + + +class Solution: + def subArrayRanges_brute(self, A: List[int]) -> int: + """ + Get a range -> get the min and max + Get all subarrays -> iterate i and j, keep tracks of min and max -> O(N^2) + + Get a range over a sliding window -> monotonic queue? + + Brute force + """ + N = len(A) + ret = 0 + for i in range(N): + mini = A[i] + maxa = A[i] + for j in range(i+1, N): + mini = min(mini, A[j]) + maxa = max(maxa, A[j]) + ret += maxa - mini + + return ret + + def subArrayRanges(self, A: List[int]) -> int: + """ + Get a range -> get the min and max + Get all subarrays -> iterate i and j, keep tracks of min and max -> O(N^2) + \sum{range} + = \sum{max - min} + = \sum max - \sum min + \sum min + = #times of min * min val + = #times of local min * min val + <- find the boundary of local min + <- monotonic stk + """ + N = len(A) + ret = 0 + + sum_max = 0 + stk = [] # monotonically decreasing stk for local max + A.append(sys.maxsize) + for i in range(N+1): + while stk and A[stk[-1]] < A[i]: + mid = stk.pop() + lo = stk[-1] if stk else -1 + # times: [mid, i), (lo, mid] + cnt = (i - mid) * (mid - lo) + sum_max += cnt * A[mid] + stk.append(i) + A.pop() + + sum_min = 0 + stk = [] # monotonically increasing stk for local min + A.append(-sys.maxsize-1) + for i in range(N+1): + while stk and A[stk[-1]] > A[i]: + mid = stk.pop() + lo = stk[-1] if stk else -1 + # times: [mid, i), (lo, mid] + cnt = (i-mid) * (mid - lo) + sum_min += cnt * A[mid] + stk.append(i) + A.pop() + + return sum_max - sum_min diff --git a/2289 Steps to Make Array Non-decreasing.py b/2289 Steps to Make Array Non-decreasing.py new file mode 100644 index 0000000..2a7641e --- /dev/null +++ b/2289 Steps to Make Array Non-decreasing.py @@ -0,0 +1,108 @@ +""" +You are given a 0-indexed integer array nums. In one step, remove all elements nums[i] where nums[i - 1] > nums[i] for all 0 < i < nums.length. + +Return the number of steps performed until nums becomes a non-decreasing array. + + + +Example 1: + +Input: nums = [5,3,4,4,7,3,6,11,8,5,11] +Output: 3 +Explanation: The following are the steps performed: +- Step 1: [5,3,4,4,7,3,6,11,8,5,11] becomes [5,4,4,7,6,11,11] +- Step 2: [5,4,4,7,6,11,11] becomes [5,4,7,11,11] +- Step 3: [5,4,7,11,11] becomes [5,7,11,11] +[5,7,11,11] is a non-decreasing array. Therefore, we return 3. +Example 2: + +Input: nums = [4,5,7,7,13] +Output: 0 +Explanation: nums is already a non-decreasing array. Therefore, we return 0. + + +Constraints: + +1 <= nums.length <= 10^5 +1 <= nums[i] <= 10^9 +""" +class Solution: + def totalSteps_error(self, A: List[int]) -> int: + """ + use a stack + remove A[i] when A[i-1] > A[i] + it becomes A[lo] < A[i] + Keep a monotonically increasing stack + """ + maxa = 0 + stk = [] # asc stack + cur = 0 + for a in A: + if stk and stk[-1] > a: + cur += 1 + continue + + stk.append(a) + maxa = max(maxa, cur) + cur = 0 + + maxa = max(maxa, cur) + return maxa + + def totalSteps_error(self, A: List[int]) -> int: + """ + How to count the steps? + Find the next larger on the left + """ + N = len(A) + # next larger on the left + L = [i for i in range(N)] + # pending element has yet to find the larger + # scanning from right to left + stk = [] # monotoicallycall decreasing + for i in range(N-1, -1, -1): + while stk and A[stk[-1]] < A[i]: + mid = stk.pop() + L[mid] = i + stk.append(i) + + maxa = 0 + for i, v in enumerate(L): + maxa = max(maxa, i - v) + + return maxa + + def totalSteps(self, A: List[int]) -> int: + """ + How to count the steps? + Find the next larger on the left? + + Requrie DP + Let F[i] be the number of steps A[i] can remove item + + When A[i] is removing A[j] when j > i and A[i] > A[j] + F[i] += 1 + remaining = F[pi] - F[i] + if remaining > 0: + F[i] += remaining + essentially, F[i] = max(F[i], F[j]) + + Example: [14, 13, 2, 6, 13] + """ + N = len(A) + F = [0 for _ in range(N)] + # To find the next larger item + # Store the pending element has yet not find next larger + # monotonically decreasing + stk = [] + for i in range(N-1, -1, -1): + while stk and A[stk[-1]] < A[i]: + pi = stk.pop() + F[i] += 1 + remaining = F[pi] - F[i] + if remaining > 0: + F[i] += remaining + # F[i] = max(F[i], F[pi]) + stk.append(i) + + return max(F) diff --git a/2342 Max Sum of a Pair With Equal Sum of Digits.py b/2342 Max Sum of a Pair With Equal Sum of Digits.py new file mode 100644 index 0000000..19a87ae --- /dev/null +++ b/2342 Max Sum of a Pair With Equal Sum of Digits.py @@ -0,0 +1,55 @@ +""" +You are given a 0-indexed array nums consisting of positive integers. You can choose two indices i and j, such that i != j, and the sum of digits of the number nums[i] is equal to that of nums[j]. + +Return the maximum value of nums[i] + nums[j] that you can obtain over all possible indices i and j that satisfy the conditions. + + + +Example 1: + +Input: nums = [18,43,36,13,7] +Output: 54 +Explanation: The pairs (i, j) that satisfy the conditions are: +- (0, 2), both numbers have a sum of digits equal to 9, and their sum is 18 + 36 = 54. +- (1, 4), both numbers have a sum of digits equal to 7, and their sum is 43 + 7 = 50. +So the maximum sum that we can obtain is 54. +Example 2: + +Input: nums = [10,12,19,14] +Output: -1 +Explanation: There are no two numbers that satisfy the conditions, so we return -1. + + +Constraints: + +1 <= nums.length <= 10^5 +1 <= nums[i] <= 10^9 +""" +from collections import defaultdict + + +class Solution: + def maximumSum(self, nums: List[int]) -> int: + """ + hash map to maintain the maximum two numbers + """ + hm = defaultdict(list) + for n in nums: + # digit sum + s = 0 + cur = n + while cur > 0: + s += cur % 10 + cur //= 10 + if len(hm[s]) < 2: + hm[s].append(n) + else: + if n > min(hm[s]): + hm[s] = [max(hm[s]), n] + + maxa = -1 + for lst in hm.values(): + if len(lst) == 2: + maxa = max(maxa, sum(lst)) + + return maxa diff --git a/2762 Continuous Subarrays.py b/2762 Continuous Subarrays.py new file mode 100644 index 0000000..ccd1d39 --- /dev/null +++ b/2762 Continuous Subarrays.py @@ -0,0 +1,109 @@ +""" +You are given a 0-indexed integer array nums. A subarray of nums is called continuous if: + +Let i, i + 1, ..., j be the indices in the subarray. Then, for each pair of indices i <= i1, i2 <= j, 0 <= |nums[i1] - nums[i2]| <= 2. +Return the total number of continuous subarrays. + +A subarray is a contiguous non-empty sequence of elements within an array. + + + +Example 1: + +Input: nums = [5,4,2,4] +Output: 8 +Explanation: +Continuous subarray of size 1: [5], [4], [2], [4]. +Continuous subarray of size 2: [5,4], [4,2], [2,4]. +Continuous subarray of size 3: [4,2,4]. +There are no subarrys of size 4. +Total continuous subarrays = 4 + 3 + 1 = 8. +It can be shown that there are no more continuous subarrays. + + +Example 2: + +Input: nums = [1,2,3] +Output: 6 +Explanation: +Continuous subarray of size 1: [1], [2], [3]. +Continuous subarray of size 2: [1,2], [2,3]. +Continuous subarray of size 3: [1,2,3]. +Total continuous subarrays = 3 + 2 + 1 = 6. + + +Constraints: + +1 <= nums.length <= 10^5 +1 <= nums[i] <= 10^9 +""" +import heapq + + +class SolutionHeap: + def continuousSubarrays(self, A: List[int]) -> int: + """ + Need to maintain max and min in the sliding window + -> heap + + + In sliding window, count of all valid subarrays ending AT j + -> j - i + 1 + """ + ret = 0 + i = 0 # starting index of the window + j = 0 # ending index of the window + h_min = [] # (a, idx) + h_max = [] # (-a, idx) + while j < len(A): + heapq.heappush(h_min, (A[j], j)) + heapq.heappush(h_max, (-A[j], j)) + while -h_max[0][0] - h_min[0][0] > 2: + # shrink + i += 1 + while h_min[0][1] < i: + heapq.heappop(h_min) + while h_max[0][1] < i: + heapq.heappop(h_max) + + ret += j-i+1 + j += 1 + + return ret + + +from collections import deque + + +class Solution: + def continuousSubarrays(self, A: List[int]) -> int: + """ + Track min & max -> Monotonic Queue + """ + q_max = deque() # monotonically decreasing + q_min = deque() # monotonically increasing + i = 0 + j = 0 + ret = 0 + while j < len(A): + # process A[j] + while q_max and A[q_max[~0]] <= A[j]: + q_max.pop() + q_max.append(j) + while q_min and A[q_min[~0]] >= A[j]: + q_min.pop() + q_min.append(j) + while q_max and q_min and A[q_max[0]] - A[q_min[0]] > 2: + # shrink to pass the breaking idx + if q_max[0] < q_min[0]: + prev = q_max.popleft() + i = prev + 1 + else: + prev = q_min.popleft() + i = prev + 1 + + ret += j - i + 1 + j += 1 + + return ret + diff --git a/2944 Minimum Number of Coins for Fruits.py b/2944 Minimum Number of Coins for Fruits.py new file mode 100644 index 0000000..451ed48 --- /dev/null +++ b/2944 Minimum Number of Coins for Fruits.py @@ -0,0 +1,117 @@ +""" +You are given an 0-indexed integer array prices where prices[i] denotes the number of coins needed to purchase the (i + 1)th fruit. + +The fruit market has the following reward for each fruit: + +If you purchase the (i + 1)th fruit at prices[i] coins, you can get any number of the next i fruits for free. +Note that even if you can take fruit j for free, you can still purchase it for prices[j - 1] coins to receive its reward. + +Return the minimum number of coins needed to acquire all the fruits. + + + +Example 1: + +Input: prices = [3,1,2] + +Output: 4 + +Explanation: + +Purchase the 1st fruit with prices[0] = 3 coins, you are allowed to take the 2nd fruit for free. +Purchase the 2nd fruit with prices[1] = 1 coin, you are allowed to take the 3rd fruit for free. +Take the 3rd fruit for free. +Note that even though you could take the 2nd fruit for free as a reward of buying 1st fruit, you purchase it to receive its reward, which is more optimal. + +Example 2: + +Input: prices = [1,10,1,1] + +Output: 2 + +Explanation: + +Purchase the 1st fruit with prices[0] = 1 coin, you are allowed to take the 2nd fruit for free. +Take the 2nd fruit for free. +Purchase the 3rd fruit for prices[2] = 1 coin, you are allowed to take the 4th fruit for free. +Take the 4th fruit for free. +Example 3: + +Input: prices = [26,18,6,12,49,7,45,45] + +Output: 39 + +Explanation: + +Purchase the 1st fruit with prices[0] = 26 coin, you are allowed to take the 2nd fruit for free. +Take the 2nd fruit for free. +Purchase the 3rd fruit for prices[2] = 6 coin, you are allowed to take the 4th, 5th and 6th (the next three) fruits for free. +Take the 4th fruit for free. +Take the 5th fruit for free. +Purchase the 6th fruit with prices[5] = 7 coin, you are allowed to take the 8th and 9th fruit for free. +Take the 7th fruit for free. +Take the 8th fruit for free. +Note that even though you could take the 6th fruit for free as a reward of buying 3rd fruit, you purchase it to receive its reward, which is more optimal. + + + +Constraints: + +1 <= prices.length <= 1000 +1 <= prices[i] <= 10&5 +""" +import sys +import math +from collections import deque + + +class Solution: + def minimumCoinsLoop(self, A: List[int]) -> int: + """ + F[i] be the minimum to acuquire all A[:i+1] and BUY A[i] + F[i] = min( + F[i-1] + ... + F[j] + ) + A[i] + where j + (j + 1) >= i - 1 + j >= math.ceil(i / 2) - 1 + + In the end, return min(F[j..N-1]) + j + (j + 1) >= N - 1 + j >= math.ceil(N / 2) - 1 + """ + N = len(A) + F = [sys.maxsize for _ in range(N)] + F[0] = A[0] + for i in range(1, N): + for j in range(math.ceil(i/2) - 1 , i): + F[i] = min(F[i], F[j] + A[i]) + + return min(F[i] for i in range(math.ceil(N/2)-1, N)) + + def minimumCoins(self, A: List[int]) -> int: + """ + DP formulation see above + + Use monotonic queue to calculate min(F[i-1]...F[j]) + """ + N = len(A) + F = [sys.maxsize for _ in range(N)] + F[0] = A[0] + q_min = deque() # keep track 1st, 2nd 3rd... min, monotonically increasing + for i in range(1, N): + # process A[i-1] + while q_min and F[q_min[~0]] >= F[i-1]: + q_min.pop() + q_min.append(i-1) # we need to put i-1 in the queue + + # proess left + while q_min and q_min[0] < math.ceil(i/2) - 1: + q_min.popleft() + + F[i] = F[q_min[0]] + A[i] + + return min(F[i] for i in range(math.ceil(N/2) - 1, N)) + + \ No newline at end of file diff --git a/3092 Most Frequent IDs.py b/3092 Most Frequent IDs.py new file mode 100644 index 0000000..a393331 --- /dev/null +++ b/3092 Most Frequent IDs.py @@ -0,0 +1,69 @@ +""" +The problem involves tracking the frequency of IDs in a collection that changes over time. You have two integer arrays, nums and freq, of equal length n. Each element in nums represents an ID, and the corresponding element in freq indicates how many times that ID should be added to or removed from the collection at each step. + +Addition of IDs: If freq[i] is positive, it means freq[i] IDs with the value nums[i] are added to the collection at step i. +Removal of IDs: If freq[i] is negative, it means -freq[i] IDs with the value nums[i] are removed from the collection at step i. +Return an array ans of length n, where ans[i] represents the count of the most frequent ID in the collection after the ith step. If the collection is empty at any step, ans[i] should be 0 for that step. + + + +Example 1: + +Input: nums = [2,3,2,1], freq = [3,2,-3,1] + +Output: [3,3,2,2] + +Explanation: + +After step 0, we have 3 IDs with the value of 2. So ans[0] = 3. +After step 1, we have 3 IDs with the value of 2 and 2 IDs with the value of 3. So ans[1] = 3. +After step 2, we have 2 IDs with the value of 3. So ans[2] = 2. +After step 3, we have 2 IDs with the value of 3 and 1 ID with the value of 1. So ans[3] = 2. + +Example 2: + +Input: nums = [5,5,3], freq = [2,-2,1] + +Output: [2,0,1] + +Explanation: + +After step 0, we have 2 IDs with the value of 5. So ans[0] = 2. +After step 1, there are no IDs. So ans[1] = 0. +After step 2, we have 1 ID with the value of 3. So ans[2] = 1. + + + +Constraints: + +1 <= nums.length == freq.length <= 10^5 +1 <= nums[i] <= 10^5 +-10^5 <= freq[i] <= 10^5 +freq[i] != 0 +The input is generated such that the occurrences of an ID will not be negative in any step. +""" +from collections import defaultdict +import heapq + + +class Solution: + def mostFrequentIDs(self, nums: List[int], freq: List[int]) -> List[int]: + """ + Priority queue. TreeMap. Binary Search Tree with update + --> Python heap with stale check + """ + counter = defaultdict(int) + h = [] + ret = [] + for n, cnt in zip(nums, freq): + counter[n] += cnt + heapq.heappush(h, (-counter[n], n)) + while True: + c, maxa = h[0] + if c != -counter[maxa]: + heapq.heappop(h) + else: + ret.append(-c) + break + + return ret diff --git a/3191 Minimum Operations to Make Binary Array Elements Equal to One I.py b/3191 Minimum Operations to Make Binary Array Elements Equal to One I.py new file mode 100644 index 0000000..2ad0eb2 --- /dev/null +++ b/3191 Minimum Operations to Make Binary Array Elements Equal to One I.py @@ -0,0 +1,59 @@ +""" +You are given a +binary array + nums. + +You can do the following operation on the array any number of times (possibly zero): + +Choose any 3 consecutive elements from the array and flip all of them. +Flipping an element means changing its value from 0 to 1, and from 1 to 0. + +Return the minimum number of operations required to make all elements in nums equal to 1. If it is impossible, return -1. + + + +Example 1: + +Input: nums = [0,1,1,1,0,0] + +Output: 3 + +Explanation: +We can do the following operations: + +Choose the elements at indices 0, 1 and 2. The resulting array is nums = [1,0,0,1,0,0]. +Choose the elements at indices 1, 2 and 3. The resulting array is nums = [1,1,1,0,0,0]. +Choose the elements at indices 3, 4 and 5. The resulting array is nums = [1,1,1,1,1,1]. +Example 2: + +Input: nums = [0,1,1,1] + +Output: -1 + +Explanation: +It is impossible to make all elements equal to 1. + + + +Constraints: + +3 <= nums.length <= 10^5 +0 <= nums[i] <= 1 +""" +class Solution: + def minOperations(self, nums: List[int]) -> int: + """ + sliding window? odd/even number of flipping + Greedily flip the leading 0? + """ + ret = 0 + for i in range(len(nums)): + if nums[i] == 0 and i + 3 <= len(nums): + ret += 1 + for j in range(i, i+3): + nums[j] ^= 1 + + if all(n == 1 for n in nums): + return ret + + return -1 diff --git a/3192 Minimum Operations to Make Binary Array Elements Equal to One II.py b/3192 Minimum Operations to Make Binary Array Elements Equal to One II.py new file mode 100644 index 0000000..c8f4222 --- /dev/null +++ b/3192 Minimum Operations to Make Binary Array Elements Equal to One II.py @@ -0,0 +1,57 @@ +""" +You are given a +binary array + nums. + +You can do the following operation on the array any number of times (possibly zero): + +Choose any index i from the array and flip all the elements from index i to the end of the array. +Flipping an element means changing its value from 0 to 1, and from 1 to 0. + +Return the minimum number of operations required to make all elements in nums equal to 1. + + + +Example 1: + +Input: nums = [0,1,1,0,1] + +Output: 4 + +Explanation: +We can do the following operations: + +Choose the index i = 1. The resulting array will be nums = [0,0,0,1,0]. +Choose the index i = 0. The resulting array will be nums = [1,1,1,0,1]. +Choose the index i = 4. The resulting array will be nums = [1,1,1,0,0]. +Choose the index i = 3. The resulting array will be nums = [1,1,1,1,1]. +Example 2: + +Input: nums = [1,0,0,0] + +Output: 1 + +Explanation: +We can do the following operation: + +Choose the index i = 1. The resulting array will be nums = [1,1,1,1]. + + +Constraints: + +1 <= nums.length <= 10^5 +0 <= nums[i] <= 1 +""" +class Solution: + def minOperations(self, nums: List[int]) -> int: + """ + The only way to change nums[0] to 1 is by performing an operation + + Greedily flip the bit, check the counter of flips is even/odd + """ + ret = 0 + for i in range(len(nums)): + if nums[i] == ret % 2: # nums[i] == 0 and ret % 2 == 0 + ret += 1 + + return ret diff --git a/3195 Find the Minimum Area to Cover All Ones I.py b/3195 Find the Minimum Area to Cover All Ones I.py new file mode 100644 index 0000000..3789eea --- /dev/null +++ b/3195 Find the Minimum Area to Cover All Ones I.py @@ -0,0 +1,77 @@ +""" +You are given a 2D binary array grid. Find a rectangle with horizontal and vertical sides with the smallest area, such that all the 1's in grid lie inside this rectangle. + +Return the minimum possible area of the rectangle. + + + +Example 1: + +Input: grid = [[0,1,0],[1,0,1]] + +Output: 6 + +Explanation: + + + +The smallest rectangle has a height of 2 and a width of 3, so it has an area of 2 * 3 = 6. + +Example 2: + +Input: grid = [[1,0],[0,0]] + +Output: 1 + +Explanation: + + + +The smallest rectangle has both height and width 1, so its area is 1 * 1 = 1. + + + +Constraints: + +1 <= grid.length, grid[i].length <= 1000 +grid[i][j] is either 0 or 1. +The input is generated such that there is at least one 1 in grid. +""" +class Solution: + def minimumArea(self, grid: List[List[int]]) -> int: + """ + projection + O(N^2) + """ + m = len(grid) + n = len(grid[0]) + H = [0 for _ in range(n)] + V = [0 for _ in range(m)] + for i in range(m): + for j in range(n): + if grid[i][j] == 1: + V[i] = 1 + H[j] = 1 + + return self.find_l(H) * self.find_l(V) + + def find_l(self, A): + m = len(A) + lo = -1 # index of last 0 + for i in range(m): + if A[i] == 1: + break + else: + lo = i + + hi = m + for i in range(m-1, -1, -1): + if A[i] == 1: + break + else: + hi = i + + if lo < hi: + return (hi-1) - (lo+1) + 1 + + return 0 diff --git a/3212 Count Submatrices With Equal Frequency of X and Y.py b/3212 Count Submatrices With Equal Frequency of X and Y.py new file mode 100644 index 0000000..d75713c --- /dev/null +++ b/3212 Count Submatrices With Equal Frequency of X and Y.py @@ -0,0 +1,160 @@ +""" +Given a 2D character matrix grid, where grid[i][j] is either 'X', 'Y', or '.', return the number of submatrices that contain: + +grid[0][0] +an equal frequency of 'X' and 'Y'. +at least one 'X'. + + +Example 1: + +Input: grid = [["X","Y","."],["Y",".","."]] + +Output: 3 + +Explanation: + + + +Example 2: + +Input: grid = [["X","X"],["X","Y"]] + +Output: 0 + +Explanation: + +No submatrix has an equal frequency of 'X' and 'Y'. + +Example 3: + +Input: grid = [[".","."],[".","."]] + +Output: 0 + +Explanation: + +No submatrix has at least one 'X'. + + + +Constraints: + +1 <= grid.length, grid[i].length <= 1000 +grid[i][j] is either 'X', 'Y', or '.'. +""" +class Solution: + def numberOfSubmatrices_error(self, mat: List[List[str]]) -> int: + """ + 1D: initialize as None, +1, -1, the count the total numbers of 0 + [X, Y, .] + + [1, 0, 0] + + 2D: project to 1D, project by column + [X, Y, .] + [Y, X, .] + + [0, 0, .] + + Error: poor handle of . + """ + M = len(mat) + N = len(mat) + ret = 0 + for lo in range(M): + # frequency for mat[lo:hi][j] + freq_cols = [None for _ in range(N)] + for hi in range(lo+1, M+1): + for j in range(N): + freq_cols[j] = self.acc(freq_cols[j], self.val(mat[hi-1][j])) + + F = [0 for _ in range(N+1)] + for j in range(1, N+1): + cur = self.acc(None, self.val(freq_cols[j-1])) + F[j] = F[j-1] + 1 if cur == 0 else 0 + + ret += F[j] + + return ret + + def val(self, a): + if a == "X": + return 1 + elif a == "Y": + return -1 + else: + None + + def acc(self, a, b): + if a == None: + return self.val(b) + else: + a += self.val(b) if self.val(b) is not None else 0 + + def numberOfSubmatrices_error_2(self, mat: List[List[str]]) -> int: + """ + To handle ., cout the submatrix with only "." + + Error: must contain grid[0][0]. This solution counts all submatrices + """ + vals = { + "X": 1, + "Y": -1, + ".": 0, + } + + M = len(mat) + N = len(mat) + ret = 0 + for lo in range(M): + # frequency for mat[lo:hi][j] + freq_cols = [0 for _ in range(N)] + is_dots_cols = [True for _ in range(N)] + for hi in range(lo+1, M+1): + for j in range(N): + freq_cols[j] += vals[mat[hi-1][j]] + is_dots_cols[j] &= mat[hi-1][j] == "." + + F = [0 for _ in range(N+1)] + D = [0 for _ in range(N+1)] + for j in range(1, N+1): + F[j] = F[j-1] + 1 if freq_cols[j-1] == 0 else 0 + D[j] = D[j-1] + 1 if is_dots_cols[j-1] else 0 + ret += max(0, F[j] - D[j]) + + return ret + + def numberOfSubmatrices(self, mat: List[List[str]]) -> int: + """ + Project 2D to 1D, project by column + + To handle ., cout the submatrix with only "." + To contain grid[0][0], just count number of 0s + """ + vals = { + "X": 1, + "Y": -1, + ".": 0, + } + + M = len(mat) + N = len(mat[0]) + ret = 0 + + freq_cols = [0 for _ in range(N)] + is_dots_cols = [True for _ in range(N)] + for i in range(M): + for j in range(N): + freq_cols[j] += vals[mat[i][j]] + is_dots_cols[j] &= mat[i][j] == "." + + all_dots = True + acc = 0 + for j in range(N): + all_dots &= is_dots_cols[j] + acc += freq_cols[j] + if acc == 0 and not all_dots: + ret += 1 + + return ret diff --git a/3271 Hash Divided String.py b/3271 Hash Divided String.py new file mode 100644 index 0000000..bb48838 --- /dev/null +++ b/3271 Hash Divided String.py @@ -0,0 +1,64 @@ +""" +You are given a string s of length n and an integer k, where n is a multiple of k. Your task is to hash the string s into a new string called result, which has a length of n / k. + +First, divide s into n / k +substrings +, each with a length of k. Then, initialize result as an empty string. + +For each substring in order from the beginning: + +The hash value of a character is the index of that character in the English alphabet (e.g., 'a' → 0, 'b' → 1, ..., 'z' → 25). +Calculate the sum of all the hash values of the characters in the substring. +Find the remainder of this sum when divided by 26, which is called hashedChar. +Identify the character in the English lowercase alphabet that corresponds to hashedChar. +Append that character to the end of result. +Return result. + + + +Example 1: + +Input: s = "abcd", k = 2 + +Output: "bf" + +Explanation: + +First substring: "ab", 0 + 1 = 1, 1 % 26 = 1, result[0] = 'b'. + +Second substring: "cd", 2 + 3 = 5, 5 % 26 = 5, result[1] = 'f'. + +Example 2: + +Input: s = "mxz", k = 3 + +Output: "i" + +Explanation: + +The only substring: "mxz", 12 + 23 + 25 = 60, 60 % 26 = 8, result[0] = 'i'. + + + +Constraints: + +1 <= k <= 100 +k <= s.length <= 1000 +s.length is divisible by k. +s consists only of lowercase English letters. +""" +class Solution: + def stringHash(self, s: str, k: int) -> str: + """ + just do the operation + """ + ret = [] + cur = 0 + for i in range(len(s)): + cur += ord(s[i]) - ord('a') + if (i+1) % k == 0: + ret.append(chr(cur % 26 + ord('a'))) + cur = 0 + + return "".join(ret) + \ No newline at end of file diff --git a/3282 Reach End of Array With Max Score.py b/3282 Reach End of Array With Max Score.py new file mode 100644 index 0000000..4ed6e65 --- /dev/null +++ b/3282 Reach End of Array With Max Score.py @@ -0,0 +1,68 @@ +""" +You are given an integer array nums of length n. + +Your goal is to start at index 0 and reach index n - 1. You can only jump to indices greater than your current index. + +The score for a jump from index i to index j is calculated as (j - i) * nums[i]. + +Return the maximum possible total score by the time you reach the last index. + + + +Example 1: + +Input: nums = [1,3,1,5] + +Output: 7 + +Explanation: + +First, jump to index 1 and then jump to the last index. The final score is 1 * 1 + 2 * 3 = 7. + +Example 2: + +Input: nums = [4,3,1,3,2] + +Output: 16 + +Explanation: + +Jump directly to the last index. The final score is 4 * 4 = 16. + + + +Constraints: + +1 <= nums.length <= 10^5 +1 <= nums[i] <= 10^5 +""" +class Solution: + def findMaximumScore(self, A: List[int]) -> int: + """ + F[i] max score at A[i] + + F[i] = max(F[j] + (i - j)*A[j] for all j) + O(N^2) + + Score: (i-j) * A[i] + sum(i-j for all i, j) = len(A) - 1 + Sum of jump gap sizes is constant + + It can be proven that from each index i, the optimal solution is to jump to the nearest index j > i + such that nums[j] > nums[i]. + + Greedy: + * Solo jump vs. double jump + * maximize gap value, gap value defined by the original A[i] -> find the next larger A[j] + * as long as the next A[j] > A[i], then there is a net gain + """ + i = 0 + ret = 0 + for j in range(1, len(A)): + if A[j] > A[i] or j == len(A)-1: + ret += (j - i) * A[i] + i = j + # handle end of iteration + + return ret + diff --git a/3286 Find a Safe Walk Through a Grid.py b/3286 Find a Safe Walk Through a Grid.py new file mode 100644 index 0000000..db9a518 --- /dev/null +++ b/3286 Find a Safe Walk Through a Grid.py @@ -0,0 +1,96 @@ +""" +You are given an m x n binary matrix grid and an integer health. + +You start on the upper-left corner (0, 0) and would like to get to the lower-right corner (m - 1, n - 1). + +You can move up, down, left, or right from one cell to another adjacent cell as long as your health remains positive. + +Cells (i, j) with grid[i][j] = 1 are considered unsafe and reduce your health by 1. + +Return true if you can reach the final cell with a health value of 1 or more, and false otherwise. + + + +Example 1: + +Input: grid = [[0,1,0,0,0],[0,1,0,1,0],[0,0,0,1,0]], health = 1 + +Output: true + +Explanation: + +The final cell can be reached safely by walking along the gray cells below. + + +Example 2: + +Input: grid = [[0,1,1,0,0,0],[1,0,1,0,0,0],[0,1,1,1,0,1],[0,0,1,0,1,0]], health = 3 + +Output: false + +Explanation: + +A minimum of 4 health points is needed to reach the final cell safely. + + +Example 3: + +Input: grid = [[1,1,1],[1,0,1],[1,1,1]], health = 5 + +Output: true + +Explanation: + +The final cell can be reached safely by walking along the gray cells below. + + + +Any path that does not go through the cell (1, 1) is unsafe since your health will drop to 0 when reaching the final cell. + + + +Constraints: + +m == grid.length +n == grid[i].length +1 <= m, n <= 50 +2 <= m * n +1 <= health <= m + n +grid[i][j] is either 0 or 1. +""" +import heapq +import sys + + +class Solution: + def findSafeWalk(self, grid: List[List[int]], health: int) -> bool: + """ + F[i][j] maximum health at i, j + But four directions, not monotonic, DP may not work + visited to avoid loop + + Greedy visited 0 with heap. + N = m*n + O(N log N) + """ + m = len(grid) + n = len(grid[0]) + F = [ + [sys.maxsize for _ in range(n)] + for _ in range(m) + ] + dirs = [(0, -1), (0, 1), (-1, 0), (1, 0)] + h = [(grid[0][0], 0, 0)] # heap queue of (acc, i, j) + while h: + acc, i, j = heapq.heappop(h) + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < m and 0 <= J < n: + nxt = acc + grid[I][J] + if F[I][J] > nxt: + F[I][J] = nxt + heapq.heappush(h, (nxt, I, J)) + + return F[~0][~0] < health + \ No newline at end of file diff --git a/3290 Maximum Multiplication Score.py b/3290 Maximum Multiplication Score.py new file mode 100644 index 0000000..60a415d --- /dev/null +++ b/3290 Maximum Multiplication Score.py @@ -0,0 +1,136 @@ +""" +You are given an integer array a of size 4 and another integer array b of size at least 4. + +You need to choose 4 indices i0, i1, i2, and i3 from the array b such that i0 < i1 < i2 < i3. Your score will be equal to the value a[0] * b[i0] + a[1] * b[i1] + a[2] * b[i2] + a[3] * b[i3]. + +Return the maximum score you can achieve. + + + +Example 1: + +Input: a = [3,2,5,6], b = [2,-6,4,-5,-3,2,-7] + +Output: 26 + +Explanation: +We can choose the indices 0, 1, 2, and 5. The score will be 3 * 2 + 2 * (-6) + 5 * 4 + 6 * 2 = 26. + +Example 2: + +Input: a = [-1,4,5,-2], b = [-5,-1,-3,-2,-4] + +Output: -1 + +Explanation: +We can choose the indices 0, 1, 3, and 4. The score will be (-1) * (-5) + 4 * (-1) + 5 * (-2) + (-2) * (-4) = -1. + + + +Constraints: + +a.length == 4 +4 <= b.length <= 10^5 +-10^5 <= a[i], b[i] <= 10^5 +""" +import sys + + +class Solution: + def maxScore_error(self, a: List[int], b: List[int]) -> int: + """ + Greedy: max times max + """ + a.sort(reverse=True) + b.sort(reverse=True) + ret = 0 + for i in range(4): + ret += a[i] *b[i] + + return ret + + def maxScore_TLE(self, a: List[int], b: List[int]) -> int: + """ + backtracking + permutation to the depth of 4 + """ + maxa = [-sys.maxsize-1] + self.backtrack(a, b, 0, [], maxa) + return maxa[0] + + def backtrack(self, a, b, cur, ret, maxa): + if len(ret) == 4: + cur_maxa = 0 + for i in range(4): + cur_maxa += a[i] * ret[i] + maxa[0] = max(maxa[0], cur_maxa) + return + + for i in range(cur, len(b)): + # not choose i + self.backtrack(a, b, i+1, ret, maxa) + # choose i + ret.append(b[i]) + self.backtrack(a, b, i+1, ret, maxa) + ret.pop() + + def maxScore_error(self, a: List[int], b: List[int]) -> int: + """ + Let F[i][j] be the max score for a[:i] and b[:j] and choosing a[i-1] but may not b[j-1] + F[i][j] = a[i-1] * b[j-1] + F[i-1][j-1] + """ + F = [ + [0 for _ in range(len(b)+1)] # cannot initialize with 0 + for _ in range(len(a)+1) + ] + + for i in range(1, len(a)+1): + for j in range(1, len(b)+1): + F[i][j] = max(a[i-1] * b[j-1] + F[i-1][j-1], F[i][j-1]) + + return max(F[4][j] for j in range(4, len(b) + 1)) + + def maxScore2(self, a: List[int], b: List[int]) -> int: + """ + Let F[i][j] be the max score for a[:i] and b[:j] and choosing a[i] but may or may not b[j] + F[i][j] = max(a[i] * b[j] + F[i-1][j-1], F[i][j-1]) + + Because of complication of negative number and calculation, we cannot augment a dummy row and a column for F + """ + MIN = -sys.maxsize-1 + F = [ + [MIN for _ in range(len(b))] + for _ in range(len(a)) + ] + + for i in range(len(a)): + for j in range(i, len(b)): # cannot start with 0 + F[i][j] = max(a[i] * b[j] + (F[i-1][j-1] if i > 0 and j > 0 else 0), F[i][j-1] if j > 0 else MIN) + + return F[3][~0] + + def maxScore(self, a: List[int], b: List[int]) -> int: + """ + Let F[i][j] be the max score for a[:i] and b[:j] and choosing a[i-1] but may or may not b[j-1] + F[i][j] = max(a[i] * b[j] + F[i-1][j-1], F[i][j-1]) + """ + MIN = -sys.maxsize-1 + F = [ + [MIN for _ in range(len(b)+1)] + for _ in range(len(a)+1) + ] + for j in range(len(b)+1): + F[0][j] = 0 + + for i in range(1, len(a)+1): + for j in range(i, len(b)+1): # cannot start with 1 + F[i][j] = max( + a[i-1] * b[j-1] + F[i-1][j-1], + F[i][j-1]) + + return F[4][~0] + + + + + \ No newline at end of file diff --git a/3305 Count of Substrings Containing Every Vowel and K Consonants I.py b/3305 Count of Substrings Containing Every Vowel and K Consonants I.py new file mode 100644 index 0000000..4e18d75 --- /dev/null +++ b/3305 Count of Substrings Containing Every Vowel and K Consonants I.py @@ -0,0 +1,146 @@ +""" +You are given a string word and a non-negative integer k. + +Return the total number of +substrings + of word that contain every vowel ('a', 'e', 'i', 'o', and 'u') at least once and exactly k consonants. + + + +Example 1: + +Input: word = "aeioqq", k = 1 + +Output: 0 + +Explanation: + +There is no substring with every vowel. + +Example 2: + +Input: word = "aeiou", k = 0 + +Output: 1 + +Explanation: + +The only substring with every vowel and zero consonants is word[0..4], which is "aeiou". + +Example 3: + +Input: word = "ieaouqqieaouqq", k = 1 + +Output: 3 + +Explanation: + +The substrings with every vowel and one consonant are: + +word[0..5], which is "ieaouq". +word[6..11], which is "qieaou". +word[7..12], which is "ieaouq". + + +Constraints: + +5 <= word.length <= 250 +word consists only of lowercase English letters. +0 <= k <= word.length - 5 +""" + +from collections import defaultdict + + +class Solution: + def countOfSubstrings_error(self, word: str, k: int) -> int: + """ + sliding window of i, j + counter of aeiou and consonants counter + O(N) + + Edge case: "iqeaouqi" + """ + cons_cnt = 0 + vowel_cnt = defaultdict(int) + vowels = set([c for c in "aeiou"]) + + ret = 0 + i = -1 # A[:i] + j = 0 + for j in range(len(word)): + char = word[j] + if char in vowels: + vowel_cnt[char] += 1 + else: + cons_cnt += 1 + + while i < j and cons_cnt > k: + i += 1 + remove = word[i] + if remove in vowels: + vowel_cnt[remove] -= 1 + if vowel_cnt[remove] == 0: + del vowel_cnt[remove] + else: + cons_cnt -= 1 + + if cons_cnt == k and len(vowel_cnt) == 5: + ret += 1 + + while i < j and cons_cnt == k: + i += 1 + remove = word[i] + if remove in vowels and vowel_cnt[remove] > 1 and len(vowel_cnt) == 5: + vowel_cnt[remove] -= 1 + ret += 1 + else: + i -= 1 + break + + return ret + + def countOfSubstrings(self, word: str, k: int) -> int: + """ + count by number of substrings + ret += (i - original_i + 1) + """ + cons_cnt = 0 + vowel_cnt = defaultdict(int) + vowels = set([c for c in "aeiou"]) + + ret = 0 + i = -1 # A[:i] + original_i = -1 + for j in range(len(word)): + char = word[j] + if char in vowels: + vowel_cnt[char] += 1 + else: + cons_cnt += 1 + + while i < j and cons_cnt > k: + i += 1 + remove = word[i] + if remove in vowels: + vowel_cnt[remove] -= 1 + if vowel_cnt[remove] == 0: + del vowel_cnt[remove] + else: + cons_cnt -= 1 + original_i = i + + while i < j and cons_cnt == k: + i += 1 + remove = word[i] + if remove in vowels and vowel_cnt[remove] > 1: + vowel_cnt[remove] -= 1 + else: + i -= 1 # restore + break + + if cons_cnt == k and len(vowel_cnt) == 5: + ret += (i - original_i + 1) + + return ret + \ No newline at end of file diff --git a/3306 Count of Substrings Containing Every Vowel and K Consonants II.py b/3306 Count of Substrings Containing Every Vowel and K Consonants II.py new file mode 100644 index 0000000..a30b99c --- /dev/null +++ b/3306 Count of Substrings Containing Every Vowel and K Consonants II.py @@ -0,0 +1,93 @@ +""" +You are given a string word and a non-negative integer k. + +Return the total number of +substrings + of word that contain every vowel ('a', 'e', 'i', 'o', and 'u') at least once and exactly k consonants. + + + +Example 1: + +Input: word = "aeioqq", k = 1 + +Output: 0 + +Explanation: + +There is no substring with every vowel. + +Example 2: + +Input: word = "aeiou", k = 0 + +Output: 1 + +Explanation: + +The only substring with every vowel and zero consonants is word[0..4], which is "aeiou". + +Example 3: + +Input: word = "ieaouqqieaouqq", k = 1 + +Output: 3 + +Explanation: + +The substrings with every vowel and one consonant are: + +word[0..5], which is "ieaouq". +word[6..11], which is "qieaou". +word[7..12], which is "ieaouq". + + +Constraints: + +5 <= word.length <= 2 * 10^5 +word consists only of lowercase English letters. +0 <= k <= word.length - 5 +""" +from collections import defaultdict + + +class Solution: + def countOfSubstrings(self, word: str, k: int) -> int: + cons_cnt = 0 + vowel_cnt = defaultdict(int) + vowels = set([c for c in "aeiou"]) + + ret = 0 + i = -1 # A[:i] + original_i = -1 + for j in range(len(word)): + char = word[j] + if char in vowels: + vowel_cnt[char] += 1 + else: + cons_cnt += 1 + + while i < j and cons_cnt > k: + i += 1 + remove = word[i] + if remove in vowels: + vowel_cnt[remove] -= 1 + if vowel_cnt[remove] == 0: + del vowel_cnt[remove] + else: + cons_cnt -= 1 + original_i = i + + while i < j and cons_cnt == k: + i += 1 + remove = word[i] + if remove in vowels and vowel_cnt[remove] > 1: + vowel_cnt[remove] -= 1 + else: + i -= 1 # restore + break + + if cons_cnt == k and len(vowel_cnt) == 5: + ret += (i - original_i + 1) + + return ret \ No newline at end of file diff --git a/3309 Maximum Possible Number by Binary Concatenation.py b/3309 Maximum Possible Number by Binary Concatenation.py new file mode 100644 index 0000000..401a5ab --- /dev/null +++ b/3309 Maximum Possible Number by Binary Concatenation.py @@ -0,0 +1,76 @@ +""" +You are given an array of integers nums of size 3. + +Return the maximum possible number whose binary representation can be formed by concatenating the binary representation of all elements in nums in some order. + +Note that the binary representation of any number does not contain leading zeros. + + + +Example 1: + +Input: nums = [1,2,3] + +Output: 30 + +Explanation: + +Concatenate the numbers in the order [3, 1, 2] to get the result "11110", which is the binary representation of 30. + +Example 2: + +Input: nums = [2,8,16] + +Output: 1296 + +Explanation: + +Concatenate the numbers in the order [2, 8, 16] to get the result "10100010000", which is the binary representation of 1296. + + + +Constraints: + +nums.length == 3 +1 <= nums[i] <= 127 +""" +class Solution: + def maxGoodNumber_error(self, nums: List[int]) -> int: + """ + 11 + 101 + 10001 + 1010101 + + just sort by lexical order + + but 1 should be in front of 10 + """ + nums_bin_str = [str(bin(n))[2:] for n in nums] + nums_bin_str.sort(reverse=True) + return int("".join(nums_bin_str), 2) + + def maxGoodNumber(self, nums: List[int]) -> int: + """ + generate all permutation + """ + # '0b101' + nums_bin_str = [str(bin(n))[2:] for n in nums] + ret = [] + self.permutations(nums_bin_str, 0, ret) + return max( + [int("".join(n), 2) for n in ret] + ) + + def permutations(self, A, cur, ret): + # in-place itertools.permutations + if cur == len(A): + ret.append(list(A)) # clone + return + + for i in range(cur, len(A)): + # swap + A[cur], A[i] = A[i], A[cur] + self.permutations(A, cur+1, ret) + # restore + A[i], A[cur] = A[cur], A[i] diff --git a/3310 Remove Methods From Project.py b/3310 Remove Methods From Project.py new file mode 100644 index 0000000..6b0c491 --- /dev/null +++ b/3310 Remove Methods From Project.py @@ -0,0 +1,93 @@ +""" +You are maintaining a project that has n methods numbered from 0 to n - 1. + +You are given two integers n and k, and a 2D integer array invocations, where invocations[i] = [ai, bi] indicates that method ai invokes method bi. + +There is a known bug in method k. Method k, along with any method invoked by it, either directly or indirectly, are considered suspicious and we aim to remove them. + +A group of methods can only be removed if no method outside the group invokes any methods within it. + +Return an array containing all the remaining methods after removing all the suspicious methods. You may return the answer in any order. If it is not possible to remove all the suspicious methods, none should be removed. + + + +Example 1: + +Input: n = 4, k = 1, invocations = [[1,2],[0,1],[3,2]] + +Output: [0,1,2,3] + +Explanation: + + + +Method 2 and method 1 are suspicious, but they are directly invoked by methods 3 and 0, which are not suspicious. We return all elements without removing anything. + +Example 2: + +Input: n = 5, k = 0, invocations = [[1,2],[0,2],[0,1],[3,4]] + +Output: [3,4] + +Explanation: + + + +Methods 0, 1, and 2 are suspicious and they are not directly invoked by any other method. We can remove them. + +Example 3: + +Input: n = 3, k = 2, invocations = [[1,2],[0,1],[2,0]] + +Output: [] + +Explanation: + + + +All methods are suspicious. We can remove them. + + + +Constraints: + +1 <= n <= 105 +0 <= k <= n - 1 +0 <= invocations.length <= 2 * 105 +invocations[i] == [ai, bi] +0 <= ai, bi <= n - 1 +ai != bi +invocations[i] != invocations[j] +""" +from collections import defaultdict + + +class Solution: + def remainingMethods(self, n: int, k: int, invocations: List[List[int]]) -> List[int]: + """ + Identify all suspicious methods -> DSP + Identify any non-suspicious methods invoke the suspicious -> reverse edge to check + """ + G = defaultdict(list) + H = defaultdict(list) # reverse + for a, b in invocations: + G[a].append(b) + H[b].append(a) + + suspicious = set() + self.dfs(G, k, suspicious) + for v in suspicious: + for nbr in H[v]: + if nbr not in suspicious: + return [i for i in range(n)] + + return [i for i in range(n) if i not in suspicious] + + def dfs(self, G, cur, visited): + visited.add(cur) + for nbr in G[cur]: + if nbr not in visited: + self.dfs(G, nbr, visited) + + + \ No newline at end of file diff --git a/3316 Find Maximum Removals From Source String.py b/3316 Find Maximum Removals From Source String.py new file mode 100644 index 0000000..2d5e025 --- /dev/null +++ b/3316 Find Maximum Removals From Source String.py @@ -0,0 +1,138 @@ +""" +You are given a string source of size n, a string pattern that is a +subsequence + of source, and a sorted integer array targetIndices that contains distinct numbers in the range [0, n - 1]. + +We define an operation as removing a character at an index idx from source such that: + +idx is an element of targetIndices. +pattern remains a +subsequence + of source after removing the character. +Performing an operation does not change the indices of the other characters in source. For example, if you remove 'c' from "acb", the character at index 2 would still be 'b'. + +Return the maximum number of operations that can be performed. + + + +Example 1: + +Input: source = "abbaa", pattern = "aba", targetIndices = [0,1,2] + +Output: 1 + +Explanation: + +We can't remove source[0] but we can do either of these two operations: + +Remove source[1], so that source becomes "a_baa". +Remove source[2], so that source becomes "ab_aa". +Example 2: + +Input: source = "bcda", pattern = "d", targetIndices = [0,3] + +Output: 2 + +Explanation: + +We can remove source[0] and source[3] in two operations. + +Example 3: + +Input: source = "dda", pattern = "dda", targetIndices = [0,1,2] + +Output: 0 + +Explanation: + +We can't remove any character from source. + +Example 4: + +Input: source = "yeyeykyded", pattern = "yeyyd", targetIndices = [0,2,3,4] + +Output: 2 + +Explanation: + +We can remove source[2] and source[3] in two operations. + + + +Constraints: + +1 <= n == source.length <= 3 * 10^3 +1 <= pattern.length <= n +1 <= targetIndices.length <= n +targetIndices is sorted in ascending order. +The input is generated such that targetIndices contains distinct elements in the range [0, n - 1]. +source and pattern consist only of lowercase English letters. +The input is generated such that pattern appears as a subsequence in source. +""" +import sys + + +class Solution: + def maxRemovals_backward(self, A: str, B: str, targetIndices: List[int]) -> int: + """ + Max possiblee: Remove all targetIndices, check pattern + + Sorted array of targetIndices + From pattern, map from pattern idx -> list of source idx + + Check subsequence - two pointers + + Backward (bottom right) DP + Define source as A and pattern as B + Let F[i][j] be the max operation for A[i:] and B[j:] while satisfy the condition + F[i][j] = + * F[i+1][j] + 1 # remove A[i] + * F[i+1][j+1] # A[i] == B[j] matching, no removal + """ + target = set(targetIndices) + m = len(A) + n = len(B) + F = [ + [-sys.maxsize-1 for _ in range(n+1)] + for _ in range(m+1) + ] + F[m][n] = 0 + for i in range(m-1, -1, -1): + F[i][n] = F[i+1][n] + (1 if i in target else 0) + + for i in range(m-1, -1, -1): + for j in range(n-1, -1, -1): + F[i][j] = F[i+1][j] + (1 if i in target else 0) # remove A[i] + if A[i] == B[j]: + F[i][j] = max(F[i][j], F[i+1][j+1]) # match A[i] B[j] + + return F[0][0] + + def maxRemovals(self, A: str, B: str, targetIndices: List[int]) -> int: + """ + Forward (top left) DP + Define source as A and pattern as B + Let F[i][j] be the max operation for A[:i] and B[:j] while satisfy the condition + F[i][j] = + * F[i-1][j] + 1 # remove A[i-1] + * F[i-1][j-1] # A[i] == B[j] matching, no removal + """ + target = set(targetIndices) + m = len(A) + n = len(B) + F = [ + [-sys.maxsize-1 for _ in range(n+1)] + for _ in range(m+1) + ] + F[0][0] = 0 + for i in range(1, m+1): + F[i][0] = F[i-1][0] + (1 if i-1 in target else 0) + + for i in range(1, m+1): + for j in range(1, n+1): + F[i][j] = F[i-1][j] + (1 if i-1 in target else 0) # remove A[i] + if A[i-1] == B[j-1]: + F[i][j] = max(F[i][j], F[i-1][j-1]) # match A[i] B[j] + + return F[m][n] + diff --git a/3371 Identify the Largest Outlier in an Array.py b/3371 Identify the Largest Outlier in an Array.py new file mode 100644 index 0000000..892d42b --- /dev/null +++ b/3371 Identify the Largest Outlier in an Array.py @@ -0,0 +1,72 @@ +#!/usr/bin/python3 +""" +You are given an integer array nums. This array contains n elements, where exactly n - 2 elements are special numbers. One of the remaining two elements is the sum of these special numbers, and the other is an outlier. + +An outlier is defined as a number that is neither one of the original special numbers nor the element representing the sum of those numbers. + +Note that special numbers, the sum element, and the outlier must have distinct indices, but may share the same value. + +Return the largest potential outlier in nums. + +Example 1: + +Input: nums = [2,3,5,10] + +Output: 10 + +Explanation: + +The special numbers could be 2 and 3, thus making their sum 5 and the outlier 10. + +Example 2: + +Input: nums = [-2,-1,-3,-6,4] + +Output: 4 + +Explanation: + +The special numbers could be -2, -1, and -3, thus making their sum -6 and the outlier 4. + +Example 3: + +Input: nums = [1,1,1,1,1,5,5] + +Output: 5 + +Explanation: + +The special numbers could be 1, 1, 1, 1, and 1, thus making their sum 5 and the other 5 as the outlier. + +Constraints: + +3 <= nums.length <= 105 +-1000 <= nums[i] <= 1000 +The input is generated such that at least one potential outlier exists in nums. +""" +import sys +from collections import defaultdict +from typing import List + + +class Solution: + def getLargestOutlier(self, nums: List[int]) -> int: + """ + if remove outlier, what will be the value of array sum + """ + total = sum(nums) + hm = defaultdict(list) + for i, v in enumerate(nums): + hm[v].append(i) + + maxa = -sys.maxsize-1 + for v in nums: + remain = total - v + if remain % 2 == 0: + half = remain // 2 + if half == v and len(hm[half]) == 1: + continue + if half in hm: + maxa = max(maxa, v) + + return maxa diff --git a/3372 Maximize the Number of Target Nodes After Connecting Trees I.py b/3372 Maximize the Number of Target Nodes After Connecting Trees I.py new file mode 100644 index 0000000..62e2930 --- /dev/null +++ b/3372 Maximize the Number of Target Nodes After Connecting Trees I.py @@ -0,0 +1,88 @@ +#!/usr/bin/python3 +""" +There exist two undirected trees with n and m nodes, with distinct labels in ranges [0, n - 1] and [0, m - 1], respectively. + +You are given two 2D integer arrays edges1 and edges2 of lengths n - 1 and m - 1, respectively, where edges1[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the first tree and edges2[i] = [ui, vi] indicates that there is an edge between nodes ui and vi in the second tree. You are also given an integer k. + +Node u is target to node v if the number of edges on the path from u to v is less than or equal to k. Note that a node is always target to itself. + +Return an array of n integers answer, where answer[i] is the maximum possible number of nodes target to node i of the first tree if you have to connect one node from the first tree to another node in the second tree. + +Note that queries are independent from each other. That is, for every query you will remove the added edge before proceeding to the next query. + +Example 1: + +Input: edges1 = [[0,1],[0,2],[2,3],[2,4]], edges2 = [[0,1],[0,2],[0,3],[2,7],[1,4],[4,5],[4,6]], k = 2 + +Output: [9,7,9,8,8] + +Explanation: + +For i = 0, connect node 0 from the first tree to node 0 from the second tree. +For i = 1, connect node 1 from the first tree to node 0 from the second tree. +For i = 2, connect node 2 from the first tree to node 4 from the second tree. +For i = 3, connect node 3 from the first tree to node 4 from the second tree. +For i = 4, connect node 4 from the first tree to node 4 from the second tree. + +Example 2: + +Input: edges1 = [[0,1],[0,2],[0,3],[0,4]], edges2 = [[0,1],[1,2],[2,3]], k = 1 + +Output: [6,3,3,3,3] + +Explanation: + +For every i, connect node i of the first tree with any node of the second tree. + +Constraints: + +2 <= n, m <= 1000 +edges1.length == n - 1 +edges2.length == m - 1 +edges1[i].length == edges2[i].length == 2 +edges1[i] = [ai, bi] +0 <= ai, bi < n +edges2[i] = [ui, vi] +0 <= ui, vi < m +The input is generated such that edges1 and edges2 represent valid trees. +0 <= k <= 1000 +""" +from collections import defaultdict + + +class Solution: + def maxTargetNodes(self, edges1: List[List[int]], edges2: List[List[int]], k: int) -> List[int]: + """ + Just greedily connect to the highest cardinality. + 2nd tree only calculate k-1 + """ + n = len(edges1) + 1 + m = len(edges2) + 1 + G1 = defaultdict(list) + for u, v in edges1: + G1[u].append(v) + G1[v].append(u) + + cardinality1 = [self.getCardinality(G1, i, k, [False] * n) for i in range(n)] + + G2 = defaultdict(list) + for u, v in edges2: + G2[u].append(v) + G2[v].append(u) + + cardinality2 = [self.getCardinality(G2, i, k - 1, [False] * m) for i in range(m)] + max2 = max(cardinality2) + return [ + cardinality1[i] + max2 + for i in range(n) + ] + + def getCardinality(self, G, u, k, visited): + # dfs + cardinality = 1 + visited[u] = True + for nbr in G[u]: + if not visited[nbr] and k - 1 >= 0: + cardinality += self.getCardinality(G, nbr, k - 1, visited) + + return cardinality \ No newline at end of file diff --git a/3381 Maximum Subarray Sum With Length Divisible by K.py b/3381 Maximum Subarray Sum With Length Divisible by K.py new file mode 100644 index 0000000..8cc217a --- /dev/null +++ b/3381 Maximum Subarray Sum With Length Divisible by K.py @@ -0,0 +1,84 @@ +""" +You are given an array of integers nums and an integer k. + +Return the maximum sum of a +subarray + of nums, such that the size of the subarray is divisible by k. + + + +Example 1: + +Input: nums = [1,2], k = 1 + +Output: 3 + +Explanation: + +The subarray [1, 2] with sum 3 has length equal to 2 which is divisible by 1. + +Example 2: + +Input: nums = [-1,-2,-3,-4,-5], k = 4 + +Output: -10 + +Explanation: + +The maximum sum subarray is [-1, -2, -3, -4] which has length equal to 4 which is divisible by 4. + +Example 3: + +Input: nums = [-5,1,2,-3,4], k = 2 + +Output: 4 + +Explanation: + +The maximum sum subarray is [1, 2, -3, 4] which has length equal to 4 which is divisible by 2. + + + +Constraints: + +1 <= k <= nums.length <= 2 * 10^5 +-10^9 <= nums[i] <= 10^9 +""" +import sys + +class Solution: + def maxSubarraySum(self, A: List[int], k: int) -> int: + """ + subarray sum -> prefix sum + search by i, j incremental of k + search is TLE, do a DP: + F[i] = max subarray sum at index of size divisible by k + """ + n = len(A) + prefix = [0 for _ in range(n+1)] + for i in range(n): + prefix[i+1] = prefix[i] + A[i] + + F = [-sys.maxsize-1 for _ in range(n+1)] + for i in range(k, n+1): + F[i] = prefix[i] - prefix[i-k] + max(0, F[i-k]) + + return max(F) + + def maxSubarraySum_TLE(self, A: List[int], k: int) -> int: + """ + subarray sum -> prefix sum + search by i, j incremental of k + """ + n = len(A) + prefix = [0 for _ in range(n+1)] + for i in range(n): + prefix[i+1] = prefix[i] + A[i] + + ret = -sys.maxsize-1 + for i in range(n): + for j in range(i + k, n + 1, k): + # sum(A[i:j]) + ret = max(ret, prefix[j] - prefix[i]) + + return ret diff --git a/3393 Count Paths With the Given XOR Value.py b/3393 Count Paths With the Given XOR Value.py new file mode 100644 index 0000000..a5fd15f --- /dev/null +++ b/3393 Count Paths With the Given XOR Value.py @@ -0,0 +1,91 @@ +#!/usr/bin/python3 +""" +You are given a 2D integer array grid with size m x n. You are also given an integer k. + +Your task is to calculate the number of paths you can take from the top-left cell (0, 0) to the bottom-right cell (m - 1, n - 1) satisfying the following constraints: + +You can either move to the right or down. Formally, from the cell (i, j) you may move to the cell (i, j + 1) or to the cell (i + 1, j) if the target cell exists. +The XOR of all the numbers on the path must be equal to k. +Return the total number of such paths. + +Since the answer can be very large, return the result modulo 10^9 + 7. + + + +Example 1: + +Input: grid = [[2, 1, 5], [7, 10, 0], [12, 6, 4]], k = 11 + +Output: 3 + +Explanation: + +The 3 paths are: + +(0, 0) → (1, 0) → (2, 0) → (2, 1) → (2, 2) +(0, 0) → (1, 0) → (1, 1) → (1, 2) → (2, 2) +(0, 0) → (0, 1) → (1, 1) → (2, 1) → (2, 2) +Example 2: + +Input: grid = [[1, 3, 3, 3], [0, 3, 3, 2], [3, 0, 1, 1]], k = 2 + +Output: 5 + +Explanation: + +The 5 paths are: + +(0, 0) → (1, 0) → (2, 0) → (2, 1) → (2, 2) → (2, 3) +(0, 0) → (1, 0) → (1, 1) → (2, 1) → (2, 2) → (2, 3) +(0, 0) → (1, 0) → (1, 1) → (1, 2) → (1, 3) → (2, 3) +(0, 0) → (0, 1) → (1, 1) → (1, 2) → (2, 2) → (2, 3) +(0, 0) → (0, 1) → (0, 2) → (1, 2) → (2, 2) → (2, 3) +Example 3: + +Input: grid = [[1, 1, 1, 2], [3, 0, 3, 2], [3, 0, 2, 2]], k = 10 + +Output: 0 + + + +Constraints: + +1 <= m == grid.length <= 300 +1 <= n == grid[r].length <= 300 +0 <= grid[r][c] < 16 +0 <= k < 16 +""" +from collections import defaultdict + +MOD = int(1e9 + 7) + +class Solution: + def countPathsWithXorValue(self, grid: List[List[int]], K: int) -> int: + """ + DP with a map of counter + F[i][j] -> dict + dict: number -> count + """ + m = len(grid) + n = len(grid[0]) + F = [ + [defaultdict(int) for _ in range(n)] + for _ in range(m) + ] + for i in range(m): + for j in range(n): + k = grid[i][j] + if i == 0 and j == 0: + F[i][j][k] = 1 + continue + + if i > 0: + for v, cnt in F[i-1][j].items(): + F[i][j][k ^ v] += cnt + F[i][j][k ^ v] %= MOD + if j > 0: + for v, cnt in F[i][j-1].items(): + F[i][j][k ^ v] += cnt + F[i][j][k ^ v] %= MOD + + return F[~0][~0][K] diff --git a/3418 Maximum Amount of Money Robot Can Earn.py b/3418 Maximum Amount of Money Robot Can Earn.py new file mode 100644 index 0000000..4b5a314 --- /dev/null +++ b/3418 Maximum Amount of Money Robot Can Earn.py @@ -0,0 +1,133 @@ +""" +You are given an m x n grid. A robot starts at the top-left corner of the grid (0, 0) and wants to reach the bottom-right corner (m - 1, n - 1). The robot can move either right or down at any point in time. + +The grid contains a value coins[i][j] in each cell: + +If coins[i][j] >= 0, the robot gains that many coins. +If coins[i][j] < 0, the robot encounters a robber, and the robber steals the absolute value of coins[i][j] coins. +The robot has a special ability to neutralize robbers in at most 2 cells on its path, preventing them from stealing coins in those cells. + +Note: The robot's total coins can be negative. + +Return the maximum profit the robot can gain on the route. + + + +Example 1: + +Input: coins = [[0,1,-1],[1,-2,3],[2,-3,4]] + +Output: 8 + +Explanation: + +An optimal path for maximum coins is: + +Start at (0, 0) with 0 coins (total coins = 0). +Move to (0, 1), gaining 1 coin (total coins = 0 + 1 = 1). +Move to (1, 1), where there's a robber stealing 2 coins. The robot uses one neutralization here, avoiding the robbery (total coins = 1). +Move to (1, 2), gaining 3 coins (total coins = 1 + 3 = 4). +Move to (2, 2), gaining 4 coins (total coins = 4 + 4 = 8). +Example 2: + +Input: coins = [[10,10,10],[10,10,10]] + +Output: 40 + +Explanation: + +An optimal path for maximum coins is: + +Start at (0, 0) with 10 coins (total coins = 10). +Move to (0, 1), gaining 10 coins (total coins = 10 + 10 = 20). +Move to (0, 2), gaining another 10 coins (total coins = 20 + 10 = 30). +Move to (1, 2), gaining the final 10 coins (total coins = 30 + 10 = 40). + + +Constraints: + +m == coins.length +n == coins[i].length +1 <= m, n <= 500 +-1000 <= coins[i][j] <= 1000 +""" +import sys + + +class Solution: + def maximumAmount_error(self, M: List[List[int]]) -> int: + """ + Let F[i][j][k] be the max amount at M[i-1][j-1] with k-1 number of neutralization + + F[i][j][k] = max( + F[i-1][j][k] + M[i-1][j-1], + F[i][j-1][k] + M[i-1][j-1], + F[i-1][j][k-1], + F[i][j-1][k-1], + ) + """ + m = len(M) + n = len(M[0]) + F = [ + [ + [ + 0 + for _ in range(4) # error here + ] + for _ in range(n+1) + ] + for _ in range(m+1) + ] + for i in range(m+1): + for j in range(n+1): + F[i][j][0] = -sys.maxsize-1 + + for i in range(1, m+1): + for j in range(1, n+1): + for k in range(1, 4): + F[i][j][k] = max( + F[i-1][j][k] + M[i-1][j-1], + F[i][j-1][k] + M[i-1][j-1], + F[i-1][j][k-1], + F[i][j-1][k-1], + ) + + return max(F[m][n]) + + def maximumAmount(self, M: List[List[int]]) -> int: + """ + Let F[i][j][k] be the max amount at M[i-1][j-1] with k-1 number of neutralization + + F[i][j][k] = max( + F[i-1][j][k] + M[i-1][j-1], + F[i][j-1][k] + M[i-1][j-1], + F[i-1][j][k-1], + F[i][j-1][k-1], + ) + """ + m = len(M) + n = len(M[0]) + F = [ + [ + [ + -sys.maxsize-1 + for _ in range(4) + ] + for _ in range(n+1) + ] + for _ in range(m+1) + ] + # prepare for M[0][0] + F[0][1][1] = 0 + F[1][0][1] = 0 + + for i in range(1, m+1): + for j in range(1, n+1): + for k in range(1, 4): + F[i][j][k] = max( + F[i-1][j][k] + M[i-1][j-1], + F[i][j-1][k] + M[i-1][j-1], + F[i-1][j][k-1], + F[i][j-1][k-1], + ) + return max(F[m][n]) diff --git a/3419 Minimize the Maximum Edge Weight of Graph.py b/3419 Minimize the Maximum Edge Weight of Graph.py new file mode 100644 index 0000000..ebbc750 --- /dev/null +++ b/3419 Minimize the Maximum Edge Weight of Graph.py @@ -0,0 +1,107 @@ +#!/usr/bin/python3 +""" +You are given two integers, n and threshold, as well as a directed weighted graph of n nodes numbered from 0 to n - 1. The graph is represented by a 2D integer array edges, where edges[i] = [Ai, Bi, Wi] indicates that there is an edge going from node Ai to node Bi with weight Wi. + +You have to remove some edges from this graph (possibly none), so that it satisfies the following conditions: + +Node 0 must be reachable from all other nodes. +The maximum edge weight in the resulting graph is minimized. +Each node has at most threshold outgoing edges. +Return the minimum possible value of the maximum edge weight after removing the necessary edges. If it is impossible for all conditions to be satisfied, return -1. + + + +Example 1: + +Input: n = 5, edges = [[1,0,1],[2,0,2],[3,0,1],[4,3,1],[2,1,1]], threshold = 2 + +Output: 1 + +Explanation: + + + +Remove the edge 2 -> 0. The maximum weight among the remaining edges is 1. + +Example 2: + +Input: n = 5, edges = [[0,1,1],[0,2,2],[0,3,1],[0,4,1],[1,2,1],[1,4,1]], threshold = 1 + +Output: -1 + +Explanation: + +It is impossible to reach node 0 from node 2. + +Example 3: + +Input: n = 5, edges = [[1,2,1],[1,3,3],[1,4,5],[2,3,2],[3,4,2],[4,0,1]], threshold = 1 + +Output: 2 + +Explanation: + + + +Remove the edges 1 -> 3 and 1 -> 4. The maximum weight among the remaining edges is 2. + +Example 4: + +Input: n = 5, edges = [[1,2,1],[1,3,3],[1,4,5],[2,3,2],[4,0,1]], threshold = 1 + +Output: -1 + + + +Constraints: + +2 <= n <= 10^5 +1 <= threshold <= n - 1 +1 <= edges.length <= min(105, n * (n - 1) / 2). +edges[i].length == 3 +0 <= Ai, Bi < n +Ai != Bi +1 <= Wi <= 10^6 +There may be multiple edges between a pair of nodes, but they must have unique weights. +""" +from collections import defaultdict +import sys + +class Solution: + def minMaxWeight(self, n: int, edges: List[List[int]], threshold: int) -> int: + """ + Node 0 must be reachable -> inverse the direction s.t. from 0 we can reach every node + Maximum Edge -> all edge above max edge are removed + Minimize the maxmium edge -> binary serach the maximum edge + """ + G = defaultdict(lambda: defaultdict(lambda: sys.maxsize)) + W = set() + for u, v, w in edges: + G[v][u] = min(w, G[v][u]) # handle duplicated edge + W.add(w) + + W = list(W) + W.sort() + + lo = 0 + hi = len(W) + while lo < hi: + mid = (lo + hi) // 2 + visited = [False for _ in range(n)] + self.dfs(G, 0, W[mid], visited) + if all(visited): # go left + hi = mid + else: # go right + lo = mid + 1 + + ret = hi # last found + if ret < len(W): + return W[ret] + else: + return -1 + + def dfs(self, G, o, w, visited): + visited[o] = True + for nbr in G[o].keys(): + if G[o][nbr] <= w and not visited[nbr]: + self.dfs(G, nbr, w, visited) diff --git a/3424 Minimum Cost to Make Arrays Identical.py b/3424 Minimum Cost to Make Arrays Identical.py new file mode 100644 index 0000000..744cfa2 --- /dev/null +++ b/3424 Minimum Cost to Make Arrays Identical.py @@ -0,0 +1,66 @@ +""" +You are given two integer arrays arr and brr of length n, and an integer k. You can perform the following operations on arr any number of times: + +Split arr into any number of contiguous +subarrays + and rearrange these subarrays in any order. This operation has a fixed cost of k. +Choose any element in arr and add or subtract a positive integer x to it. The cost of this operation is x. + +Return the minimum total cost to make arr equal to brr. + + + +Example 1: + +Input: arr = [-7,9,5], brr = [7,-2,-5], k = 2 + +Output: 13 + +Explanation: + +Split arr into two contiguous subarrays: [-7] and [9, 5] and rearrange them as [9, 5, -7], with a cost of 2. +Subtract 2 from element arr[0]. The array becomes [7, 5, -7]. The cost of this operation is 2. +Subtract 7 from element arr[1]. The array becomes [7, -2, -7]. The cost of this operation is 7. +Add 2 to element arr[2]. The array becomes [7, -2, -5]. The cost of this operation is 2. +The total cost to make the arrays equal is 2 + 2 + 7 + 2 = 13. + +Example 2: + +Input: arr = [2,1], brr = [2,1], k = 0 + +Output: 0 + +Explanation: + +Since the arrays are already equal, no operations are needed, and the total cost is 0. + + + +Constraints: + +1 <= arr.length == brr.length <= 10^5 +0 <= k <= 2 * 10^10 +-10^5 <= arr[i] <= 10^5 +-10^5 <= brr[i] <= 10^5 +""" +class Solution: + def minCost(self, arr: List[int], brr: List[int], k: int) -> int: + """ + greedy sort all + x = |a - b| + minimize the sum of |a-b| of two numbers + """ + mini = self.match_cost(arr, brr) + + arr.sort() + brr.sort() + mini = min(mini, k + self.match_cost(arr, brr)) + + return mini + + def match_cost(self, A, B): + ret = 0 + for a, b in zip(A, B): + ret += abs(a - b) + + return ret \ No newline at end of file diff --git a/3428 Maximum and Minimum Sums of at Most Size K Subsequences.py b/3428 Maximum and Minimum Sums of at Most Size K Subsequences.py new file mode 100644 index 0000000..f020b97 --- /dev/null +++ b/3428 Maximum and Minimum Sums of at Most Size K Subsequences.py @@ -0,0 +1,97 @@ +""" +You are given an integer array nums and a positive integer k. Return the sum of the maximum and minimum elements of all +subsequences of nums with at most k elements. + +Since the answer may be very large, return it modulo 10^9 + 7. + +Example 1: + +Input: nums = [1,2,3], k = 2 + +Output: 24 + +Explanation: + +The subsequences of nums with at most 2 elements are: + +Subsequence Minimum Maximum Sum +[1] 1 1 2 +[2] 2 2 4 +[3] 3 3 6 +[1, 2] 1 2 3 +[1, 3] 1 3 4 +[2, 3] 2 3 5 +Final Total 24 +The output would be 24. + +Example 2: + +Input: nums = [5,0,6], k = 1 + +Output: 22 + +Explanation: + +For subsequences with exactly 1 element, the minimum and maximum values are the element itself. Therefore, the total is 5 + 5 + 0 + 0 + 6 + 6 = 22. + +Example 3: + +Input: nums = [1,1,1], k = 2 + +Output: 12 + +Explanation: + +The subsequences [1, 1] and [1] each appear 3 times. For all of them, the minimum and maximum are both 1. Thus, the total is 12. + + + +Constraints: + +1 <= nums.length <= 10^5 +0 <= nums[i] <= 10^9 +1 <= k <= min(70, nums.length) +""" +MOD = int(1e9 + 7) + +class Solution: + def minMaxSums(self, A: List[int], K: int) -> int: + """ + sort + calcualte how many subsequence between i, j with i and j selected. n \choose r + Select i, j is O(N^2) + + Contribution based, when select A[i]: + * A[i] is the min + * A[i] is the max + """ + A.sort() + + N = len(A) + nCr = [ + [0 for _ in range(K)] + for _ in range(N+1) + ] + # nCr[0][x] must be 0 where x > 0 + + nCr[0][0] = 1 + for n in range(1, N+1): + nCr[n][0] = 1 + for r in range(1, K): + nCr[n][r] = nCr[n-1][r-1] + nCr[n-1][r] + + ret = 0 + for i in range(N): + # choose A[i] + sequences = 0 + for r in range(K): # choose up to k-1 items + if r <= i: + sequences += nCr[i][r] + else: + break + + ret += A[i] * sequences # A[i] is the max + ret += A[~i] * sequences # A[~i] is the min + ret %= MOD + + return ret \ No newline at end of file diff --git a/3433 Count Mentions Per User.py b/3433 Count Mentions Per User.py new file mode 100644 index 0000000..7ecebe2 --- /dev/null +++ b/3433 Count Mentions Per User.py @@ -0,0 +1,130 @@ +""" +You are given an integer numberOfUsers representing the total number of users and an array events of size n x 3. + +Each events[i] can be either of the following two types: + +Message Event: ["MESSAGE", "timestampi", "mentions_stringi"] +This event indicates that a set of users was mentioned in a message at timestampi. +The mentions_stringi string can contain one of the following tokens: +id: where is an integer in range [0,numberOfUsers - 1]. There can be multiple ids separated by a single whitespace and may contain duplicates. This can mention even the offline users. +ALL: mentions all users. +HERE: mentions all online users. +Offline Event: ["OFFLINE", "timestampi", "idi"] +This event indicates that the user idi had become offline at timestampi for 60 time units. The user will automatically be online again at time timestampi + 60. +Return an array mentions where mentions[i] represents the number of mentions the user with id i has across all MESSAGE events. + +All users are initially online, and if a user goes offline or comes back online, their status change is processed before handling any message event that occurs at the same timestamp. + +Note that a user can be mentioned multiple times in a single message event, and each mention should be counted separately. + + + +Example 1: + +Input: numberOfUsers = 2, events = [["MESSAGE","10","id1 id0"],["OFFLINE","11","0"],["MESSAGE","71","HERE"]] + +Output: [2,2] + +Explanation: + +Initially, all users are online. + +At timestamp 10, id1 and id0 are mentioned. mentions = [1,1] + +At timestamp 11, id0 goes offline. + +At timestamp 71, id0 comes back online and "HERE" is mentioned. mentions = [2,2] + +Example 2: + +Input: numberOfUsers = 2, events = [["MESSAGE","10","id1 id0"],["OFFLINE","11","0"],["MESSAGE","12","ALL"]] + +Output: [2,2] + +Explanation: + +Initially, all users are online. + +At timestamp 10, id1 and id0 are mentioned. mentions = [1,1] + +At timestamp 11, id0 goes offline. + +At timestamp 12, "ALL" is mentioned. This includes offline users, so both id0 and id1 are mentioned. mentions = [2,2] + +Example 3: + +Input: numberOfUsers = 2, events = [["OFFLINE","10","0"],["MESSAGE","12","HERE"]] + +Output: [0,1] + +Explanation: + +Initially, all users are online. + +At timestamp 10, id0 goes offline. + +At timestamp 12, "HERE" is mentioned. Because id0 is still offline, they will not be mentioned. mentions = [0,1] + + + +Constraints: + +1 <= numberOfUsers <= 100 +1 <= events.length <= 100 +events[i].length == 3 +events[i][0] will be one of MESSAGE or OFFLINE. +1 <= int(events[i][1]) <= 10^5 +The number of id mentions in any "MESSAGE" event is between 1 and 100. +0 <= <= numberOfUsers - 1 +It is guaranteed that the user id referenced in the OFFLINE event is online at the time the event occurs. +""" +import heapq +from collections import defaultdict + + +class Solution: + def countMentions(self, numberOfUsers: int, events: List[List[str]]) -> List[int]: + """ + Counting without offline is easy + How to maintain offline timestamp -> event-driven algo + * Maitain a aet of users that is offline + * Use heap to maintain a set of users to be online O(n lg n) + + Debugging: + * events may be out of order -> sort + * a user can offline and online multiple times -> counter + """ + n = numberOfUsers + mentions = [0 for _ in range(n)] + offline = defaultdict(int) # set() + h = [] # heap, a set of users to be online + + events.sort(key=lambda e: (int(e[1]), 0 if e[0] == "OFFLINE" else 1)) + + for event, ts_str, content in events: + ts = int(ts_str) + if event == "MESSAGE": + if content.startswith("id"): + for s in content.split(" "): + idx = int(s[2:]) + mentions[idx] += 1 + elif content == "ALL": + for i in range(n): + mentions[i] += 1 + elif content == "HERE": + while h and h[0][0] <= ts: # heap peak + _, idx = heapq.heappop(h) + offline[idx] -= 1 + if offline[idx] == 0: + del offline[idx] + + for i in range(n): + if i not in offline: + mentions[i] += 1 + + elif event == "OFFLINE": + idx = int(content) + offline[idx] += 1 + heapq.heappush(h, (ts + 60, idx)) + + return mentions \ No newline at end of file diff --git a/3434 Maximum Frequency After Subarray Operation.py b/3434 Maximum Frequency After Subarray Operation.py new file mode 100644 index 0000000..d677dae --- /dev/null +++ b/3434 Maximum Frequency After Subarray Operation.py @@ -0,0 +1,116 @@ +""" +You are given an array nums of length n. You are also given an integer k. + +You perform the following operation on nums once: + +Select a +subarray + nums[i..j] where 0 <= i <= j <= n - 1. +Select an integer x and add x to all the elements in nums[i..j]. +Find the maximum frequency of the value k after the operation. + + + +Example 1: + +Input: nums = [1,2,3,4,5,6], k = 1 + +Output: 2 + +Explanation: + +After adding -5 to nums[2..5], 1 has a frequency of 2 in [1, 2, -2, -1, 0, 1]. + +Example 2: + +Input: nums = [10,2,3,4,5,5,4,3,2,2], k = 10 + +Output: 4 + +Explanation: + +After adding 8 to nums[1..9], 10 has a frequency of 4 in [10, 10, 11, 12, 13, 13, 12, 11, 10, 10]. + + + +Constraints: + +1 <= n == nums.length <= 10^5 +1 <= nums[i] <= 50 +1 <= k <= 50 +""" +class Solution: + def maxFrequency_TLE(self, nums: List[int], k: int) -> int: + """ + Brute foce: + * i, j subarray search, the max frequency of equal distance to k + * O(N^3) + + k is in [1, 50] + Does k actually matter? It matters for the rest of the array without operations + Define O[i]: In nums[:i], the frequency of number = k + Define F[i][j]: In nums[:i], the frequency of number = j+1 + O(N^2) + """ + n = len(nums) + O = [0 for _ in range(n+1)] + for i in range(n): + O[i+1] = O[i] + if nums[i] == k: + O[i+1] += 1 + + F = [ + [0 for _ in range(50)] + for _ in range(n+1) + ] + for i in range(n): + F[i+1] = list(F[i]) # copy + F[i+1][nums[i] - 1] += 1 + + maxa = 0 + for i in range(n): + for j in range(i+1, n+1): + # subarray nums[i:j] + cnt = 0 + cnt += O[i] + cnt += O[n] - O[j] + + m = 0 + for num in range(50): + m = max(m, F[j][num] - F[i][num]) + + maxa = max(maxa, cnt + m) + + return maxa + + def maxFrequency(self, nums: List[int], k: int) -> int: + """ + Maximum subarray frequency -> Maximum subarray sum + """ + n = len(nums) + freq_k = 0 + for n in nums: + if n == k: + freq_k += 1 + + maxa = 0 + for target in range(1, 51): + maxa = max(maxa, self.count(nums, target, k) + freq_k) + + return maxa + + def count(self, nums, target, k): + cnt = 0 + maxa = 0 + for n in nums: + if n == k: + cnt -= 1 + elif n == target: + cnt += 1 + + if cnt < 0: + cnt = 0 + + maxa = max(maxa, cnt) + + return maxa \ No newline at end of file diff --git a/3446 Sort Matrix by Diagonals.py b/3446 Sort Matrix by Diagonals.py new file mode 100644 index 0000000..b75a8f3 --- /dev/null +++ b/3446 Sort Matrix by Diagonals.py @@ -0,0 +1,97 @@ +""" +You are given an n x n square matrix of integers grid. Return the matrix such that: + +The diagonals in the bottom-left triangle (including the middle diagonal) are sorted in non-increasing order. +The diagonals in the top-right triangle are sorted in non-decreasing order. + + +Example 1: + +Input: grid = [[1,7,3],[9,8,2],[4,5,6]] + +Output: [[8,2,3],[9,6,7],[4,5,1]] + +Explanation: + + + +The diagonals with a black arrow (bottom-left triangle) should be sorted in non-increasing order: + +[1, 8, 6] becomes [8, 6, 1]. +[9, 5] and [4] remain unchanged. +The diagonals with a blue arrow (top-right triangle) should be sorted in non-decreasing order: + +[7, 2] becomes [2, 7]. +[3] remains unchanged. +Example 2: + +Input: grid = [[0,1],[1,2]] + +Output: [[2,1],[1,0]] + +Explanation: + + + +The diagonals with a black arrow must be non-increasing, so [0, 2] is changed to [2, 0]. The other diagonals are already in the correct order. + +Example 3: + +Input: grid = [[1]] + +Output: [[1]] + +Explanation: + +Diagonals with exactly one element are already in order, so no changes are needed. + + + +Constraints: + +grid.length == grid[i].length == n +1 <= n <= 10 +-105 <= grid[i][j] <= 10^5 +""" +class Solution: + def sortMatrix(self, grid: List[List[int]]) -> List[List[int]]: + """ + use a tempary list + """ + m = len(grid) + n = len(grid[0]) + for i in range(m): + r = i + c = 0 + lst = [] + while r < m and c < n: + lst.append(grid[r][c]) + r += 1 + c += 1 + + lst.sort(reverse=True) + r = i + c = 0 + while r < m and c < n: + grid[r][c] = lst[c] + r += 1 + c += 1 + + for j in range(1, n): + r = 0 + c = j + lst = [] + while r < m and c < n: + lst.append(grid[r][c]) + r += 1 + c += 1 + + lst.sort() + r = 0 + c = j + while r < m and c < n: + grid[r][c] = lst[r] + r += 1 + c += 1 + + return grid \ No newline at end of file diff --git a/3453 Separate Squares I.py b/3453 Separate Squares I.py new file mode 100644 index 0000000..21883fb --- /dev/null +++ b/3453 Separate Squares I.py @@ -0,0 +1,110 @@ +""" +You are given a 2D integer array squares. Each squares[i] = [xi, yi, li] represents the coordinates of the bottom-left point and the side length of a square parallel to the x-axis. + +Find the minimum y-coordinate value of a horizontal line such that the total area of the squares above the line equals the total area of the squares below the line. + +Answers within 10^-5 of the actual answer will be accepted. + +Note: Squares may overlap. Overlapping areas should be counted multiple times. + + + +Example 1: + +Input: squares = [[0,0,1],[2,2,1]] + +Output: 1.00000 + +Explanation: + + + +Any horizontal line between y = 1 and y = 2 will have 1 square unit above it and 1 square unit below it. The lowest option is 1. + +Example 2: + +Input: squares = [[0,0,2],[1,1,1]] + +Output: 1.16667 + +Explanation: + + + +The areas are: + +Below the line: 7/6 * 2 (Red) + 1/6 (Blue) = 15/6 = 2.5. +Above the line: 5/6 * 2 (Red) + 5/6 (Blue) = 15/6 = 2.5. +Since the areas above and below the line are equal, the output is 7/6 = 1.16667. + + + +Constraints: + +1 <= squares.length <= 5 * 10^4 +squares[i] = [xi, yi, li] +squares[i].length == 3 +0 <= xi, yi <= 10^9 +1 <= li <= 10^9 +""" +class Solution: + def separateSquares_brute(self, squares: List[List[int]]) -> float: + """ + brute force: + * bindary search for the real number space + * partition the the squares O(N) + * calcualte the areas + """ + lo = 0 + hi = 2e9 # 1e9 + 1e9 + eps = 1e-5 + while lo < hi - eps: + mid = (lo + hi) / 2 + above = 0 + below = 0 + for x, y, l in squares: + if y + l< mid: + below += l*l + elif y > mid: + above += l*l + else: + # mid between y, y+l + below += l * (mid - y) + above += l * (y+l - mid) + if below < above: + lo = mid + else: + hi = mid + + return lo + + def separateSquares(self, squares: List[List[int]]) -> float: + """ + Event driven. Line Sweep. + F(y) = AreaBelow(y) + dF/dy = slope, the rate of change of F(y) + F(y) is monotonically increasing + """ + events = [] + total_area = 0 + for x, y, l in squares: + # (y-coordiate, delta_slope) + events.append((y, l)) + events.append((y+l, -l)) + total_area += l*l + + events.sort() + target = total_area / 2 + + F = 0 + slope = 0 + prev_y = events[0][0] + for y, d_slope in events: + F += (y - prev_y) * slope + if F >= target: + return y - (F - target) / slope + + slope += d_slope + prev_y = y + + return prev_y diff --git a/3457 Eat Pizzas.py b/3457 Eat Pizzas.py new file mode 100644 index 0000000..4070045 --- /dev/null +++ b/3457 Eat Pizzas.py @@ -0,0 +1,98 @@ +""" +You are given an integer array pizzas of size n, where pizzas[i] represents the weight of the ith pizza. Every day, you eat exactly 4 pizzas. Due to your incredible metabolism, when you eat pizzas of weights W, X, Y, and Z, where W <= X <= Y <= Z, you gain the weight of only 1 pizza! + +On odd-numbered days (1-indexed), you gain a weight of Z. +On even-numbered days, you gain a weight of Y. +Find the maximum total weight you can gain by eating all pizzas optimally. + +Note: It is guaranteed that n is a multiple of 4, and each pizza can be eaten only once. + + + +Example 1: + +Input: pizzas = [1,2,3,4,5,6,7,8] + +Output: 14 + +Explanation: + +On day 1, you eat pizzas at indices [1, 2, 4, 7] = [2, 3, 5, 8]. You gain a weight of 8. +On day 2, you eat pizzas at indices [0, 3, 5, 6] = [1, 4, 6, 7]. You gain a weight of 6. +The total weight gained after eating all the pizzas is 8 + 6 = 14. + +Example 2: + +Input: pizzas = [2,1,1,1,1,1,1,1] + +Output: 3 + +Explanation: + +On day 1, you eat pizzas at indices [4, 5, 6, 0] = [1, 1, 1, 2]. You gain a weight of 2. +On day 2, you eat pizzas at indices [1, 2, 3, 7] = [1, 1, 1, 1]. You gain a weight of 1. +The total weight gained after eating all the pizzas is 2 + 1 = 3. + + + +Constraints: + +4 <= n == pizzas.length <= 2 * 10^5 +1 <= pizzas[i] <= 10^5 +n is a multiple of 4. +""" +class Solution: + def maxWeight_error(self, pizzas: List[int]) -> int: + """ + break into 4 subarray + sort each subarray + + On odd-numbered days, it is optimal to pair the smallest three and the largest one. + On even-numbered days, it is optimal to pair the smallest two and the largest two. + """ + ret = 0 + pizzas.sort() + i = 0 + j = len(pizzas) - 1 + is_odd = True + while i < j: + if is_odd: + ret += pizzas[j] + i += 3 + j -= 1 + else: + ret += pizzas[j-1] + i += 2 + j -= 2 + is_odd ^= True # XOR + + return ret + + + def maxWeight(self, pizzas: List[int]) -> int: + """ + break into 4 subarray + sort each subarray + + On odd-numbered days, it is optimal to pair the smallest three and the largest one. + On even-numbered days, it is optimal to pair the smallest two and the largest two. + + Greedily select pizzas for all odd-numbered days first. + """ + ret = 0 + pizzas.sort() + j = len(pizzas) - 1 + + days = len(pizzas) // 4 + odd_days = days // 2 + days % 2 + even_days = days - odd_days + + for _ in range(odd_days): + ret += pizzas[j] + j -= 1 + for _ in range(even_days): + ret += pizzas[j-1] + j -= 2 + + return ret + \ No newline at end of file diff --git a/853 Car Fleet.py b/853 Car Fleet.py new file mode 100644 index 0000000..6d874b3 --- /dev/null +++ b/853 Car Fleet.py @@ -0,0 +1,77 @@ +""" +There are n cars at given miles away from the starting mile 0, traveling to reach the mile target. + +You are given two integer array position and speed, both of length n, where position[i] is the starting mile of the ith car and speed[i] is the speed of the ith car in miles per hour. + +A car cannot pass another car, but it can catch up and then travel next to it at the speed of the slower car. + +A car fleet is a car or cars driving next to each other. The speed of the car fleet is the minimum speed of any car in the fleet. + +If a car catches up to a car fleet at the mile target, it will still be considered as part of the car fleet. + +Return the number of car fleets that will arrive at the destination. + + + +Example 1: + +Input: target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3] + +Output: 3 + +Explanation: + +The cars starting at 10 (speed 2) and 8 (speed 4) become a fleet, meeting each other at 12. The fleet forms at target. +The car starting at 0 (speed 1) does not catch up to any other car, so it is a fleet by itself. +The cars starting at 5 (speed 1) and 3 (speed 3) become a fleet, meeting each other at 6. The fleet moves at speed 1 until it reaches target. +Example 2: + +Input: target = 10, position = [3], speed = [3] + +Output: 1 + +Explanation: + +There is only one car, hence there is only one fleet. +Example 3: + +Input: target = 100, position = [0,2,4], speed = [4,2,1] + +Output: 1 + +Explanation: + +The cars starting at 0 (speed 4) and 2 (speed 2) become a fleet, meeting each other at 4. The car starting at 4 (speed 1) travels to 5. +Then, the fleet at 4 (speed 2) and the car at position 5 (speed 1) become one fleet, meeting each other at 6. The fleet moves at speed 1 until it reaches target. + + +Constraints: + +n == position.length == speed.length +1 <= n <= 10^5 +0 < target <= 10^6 +0 <= position[i] < target +All the values of position are unique. +0 < speed[i] <= 10^6 +""" +class Solution: + def carFleet(self, target: int, position: List[int], speed: List[int]) -> int: + """ + Epoch by epoch? + Physics simulation? + + Front car will dominate - either reach the destination itself or lead the car fleet + Know speed and distance - use time to check car behind will catch up + """ + fleets = 0 + objs = list(zip(position, speed)) + objs.sort(reverse=True) + + t = 0 # current fleet arrival time + for o, v in objs: # origin, velociy + cur = (target - o) / v + if cur > t: + fleets += 1 # new fleet + t = cur + + return fleets From a6b95a73f7455b3f787acd10bf89c2ca46b3b3b4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 21 Mar 2025 01:54:40 -0400 Subject: [PATCH 339/344] update --- ...nsufficient Nodes in Root to Leaf Paths.py | 110 ++++++++++++++++ 1104 Path In Zigzag Labelled Binary Tree.py | 43 +++++++ 1110 Delete Nodes And Return Forest.py | 68 ++++++++++ ...owest Common Ancestor of Deepest Leaves.py | 88 +++++++++++++ 1161 Maximum Level Sum of a Binary Tree.py | 66 ++++++++++ ... Elements in a Contaminated Binary Tree.py | 107 ++++++++++++++++ 1302 Deepest Leaves Sum.py | 52 ++++++++ ...All Elements in Two Binary Search Trees.py | 76 +++++++++++ ...m of Nodes with Even-Valued Grandparent.py | 64 ++++++++++ 1325 Delete Leaves With a Given Value.py | 68 ++++++++++ 1448 Count Good Nodes in Binary Tree.py | 70 +++++++++++ 2487 Remove Nodes From Linked List.py | 55 ++++++++ 2865 Beautiful Towers I.py | 119 ++++++++++++++++++ 2866 Beautiful Towers II.py | 79 ++++++++++++ 655 Print Binary Tree.py | 74 +++++++++++ 690 Employee Importance.py | 66 ++++++++++ 16 files changed, 1205 insertions(+) create mode 100644 1080 Insufficient Nodes in Root to Leaf Paths.py create mode 100644 1104 Path In Zigzag Labelled Binary Tree.py create mode 100644 1110 Delete Nodes And Return Forest.py create mode 100644 1123 Lowest Common Ancestor of Deepest Leaves.py create mode 100644 1161 Maximum Level Sum of a Binary Tree.py create mode 100644 1261 Find Elements in a Contaminated Binary Tree.py create mode 100644 1302 Deepest Leaves Sum.py create mode 100644 1305 All Elements in Two Binary Search Trees.py create mode 100644 1315 Sum of Nodes with Even-Valued Grandparent.py create mode 100644 1325 Delete Leaves With a Given Value.py create mode 100644 1448 Count Good Nodes in Binary Tree.py create mode 100644 2487 Remove Nodes From Linked List.py create mode 100644 2865 Beautiful Towers I.py create mode 100644 2866 Beautiful Towers II.py create mode 100644 655 Print Binary Tree.py create mode 100644 690 Employee Importance.py diff --git a/1080 Insufficient Nodes in Root to Leaf Paths.py b/1080 Insufficient Nodes in Root to Leaf Paths.py new file mode 100644 index 0000000..e966d1c --- /dev/null +++ b/1080 Insufficient Nodes in Root to Leaf Paths.py @@ -0,0 +1,110 @@ +""" +Given the root of a binary tree and an integer limit, delete all insufficient nodes in the tree simultaneously, and return the root of the resulting binary tree. + +A node is insufficient if every root to leaf path intersecting this node has a sum strictly less than limit. + +A leaf is a node with no children. + + + +Example 1: + + +Input: root = [1,2,3,4,-99,-99,7,8,9,-99,-99,12,13,-99,14], limit = 1 +Output: [1,2,3,4,null,null,7,8,9,null,14] +Example 2: + + +Input: root = [5,4,8,11,null,17,4,7,1,null,null,5,3], limit = 22 +Output: [5,4,8,11,null,17,4,7,null,null,null,5] +Example 3: + + +Input: root = [1,2,-3,-5,null,4,null], limit = -1 +Output: [1,null,-3,4] + + +Constraints: + +The number of nodes in the tree is in the range [1, 5000]. +-10^5 <= Node.val <= 10^5 +-10^9 <= limit <= 10^9 +""" +import sys + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + +class SolutionError: + def sufficientSubset(self, root: Optional[TreeNode], limit: int) -> Optional[TreeNode]: + """ + every path sum < limit => max(path_sums) < limit + dfs to find the max pathsum + """ + pi = TreeNode(val=0) + pi.left = root + self.dfs(pi, limit) + return pi.left + + def dfs(self, node, limit): + max_path_sum = 0 + if node.left: + left_sum = self.dfs(node.left, limit) + if left_sum < limit: + node.left = None + max_path_sum = max(max_path_sum, left_sum) + if node.right: + right_sum = self.dfs(node.right, limit) + if right_sum < limit: + node.right = None + max_path_sum = max(max_path_sum, right_sum) + + return max_path_sum + node.val + + +class Solution: + def sufficientSubset(self, root: Optional[TreeNode], limit: int) -> Optional[TreeNode]: + """ + root to path intersection this node < limit + path_sum: + 1. root to node: [root, node] + 2. node to leaf: [node, leaf] + + every path sum < limit => max path sum < limit + """ + self.limit = limit + pi = TreeNode(0) + pi.left = root + self.dfs(pi, 0) + return pi.left + + def dfs(self, node, root_sum): + """ + delete the node.left or node.right if insufficient + return the max leaf sum + """ + if not node.left and not node.right: + return node.val + + root_sum += node.val + leaf_sum = -sys.maxsize-1 + if node.left: + l = self.dfs(node.left, root_sum) + if root_sum + l < self.limit: + node.left = None + + leaf_sum = max(leaf_sum, l) + + if node.right: + r = self.dfs(node.right, root_sum) + if root_sum + r < self.limit: + node.right = None + + leaf_sum = max(leaf_sum, r) + + return leaf_sum + node.val \ No newline at end of file diff --git a/1104 Path In Zigzag Labelled Binary Tree.py b/1104 Path In Zigzag Labelled Binary Tree.py new file mode 100644 index 0000000..60066eb --- /dev/null +++ b/1104 Path In Zigzag Labelled Binary Tree.py @@ -0,0 +1,43 @@ +""" +In an infinite binary tree where every node has two children, the nodes are labelled in row order. + +In the odd numbered rows (ie., the first, third, fifth,...), the labelling is left to right, while in the even numbered rows (second, fourth, sixth,...), the labelling is right to left. + +Given the label of a node in this tree, return the labels in the path from the root of the tree to the node with that label. + +Example 1: + +Input: label = 14 +Output: [1,3,4,14] +Example 2: + +Input: label = 26 +Output: [1,2,6,10,26] + + +Constraints: + +1 <= label <= 10^6 +""" +class Solution: + def pathInZigZagTree(self, label: int) -> List[int]: + """ + Brute force, construct the tree + + If not zig zag, pi = x // 2 + If zig zag, + complement of pi = x // 2, for every level by observation + """ + stk = [label] + while stk[-1] > 1: + cur = stk[-1] >> 1 + # 2^lo <= val < 2^hi + msb = 0 + while cur >> msb > 0: + msb += 1 + delta = cur - (1 << msb - 1) + t = (1 << msb) - 1 - delta + stk.append(t) + + return stk[::-1] + \ No newline at end of file diff --git a/1110 Delete Nodes And Return Forest.py b/1110 Delete Nodes And Return Forest.py new file mode 100644 index 0000000..7038087 --- /dev/null +++ b/1110 Delete Nodes And Return Forest.py @@ -0,0 +1,68 @@ +""" +Given the root of a binary tree, each node in the tree has a distinct value. + +After deleting all nodes with a value in to_delete, we are left with a forest (a disjoint union of trees). + +Return the roots of the trees in the remaining forest. You may return the result in any order. + + + +Example 1: + + +Input: root = [1,2,3,4,5,6,7], to_delete = [3,5] +Output: [[1,2,null,4],[6],[7]] +Example 2: + +Input: root = [1,2,4,null,3], to_delete = [3] +Output: [[1,2,4]] + + +Constraints: + +The number of nodes in the given tree is at most 1000. +Each node has a distinct value between 1 and 1000. +to_delete.length <= 1000 +to_delete contains distinct values between 1 and 1000. +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def delNodes(self, root: Optional[TreeNode], to_delete: List[int]) -> List[TreeNode]: + """ + after deletion, children become new tree + """ + pi = TreeNode() + pi.left = root + acc = [] + self.dfs(pi, True, pi.left, set(to_delete), acc) + if pi.left: + acc.append(pi.left) + + return acc + + def dfs(self, pi, is_left, cur, to_delete, acc): + if cur.left: + self.dfs(cur, True, cur.left, to_delete, acc) + if cur.right: + self.dfs(cur, False, cur.right, to_delete, acc) + + # after dfs delete the child + if cur.val in to_delete: + if cur.left: + acc.append(cur.left) + + if cur.right: + acc.append(cur.right) + + if is_left: + pi.left = None + else: + pi.right = None + \ No newline at end of file diff --git a/1123 Lowest Common Ancestor of Deepest Leaves.py b/1123 Lowest Common Ancestor of Deepest Leaves.py new file mode 100644 index 0000000..a2d0b4f --- /dev/null +++ b/1123 Lowest Common Ancestor of Deepest Leaves.py @@ -0,0 +1,88 @@ +""" +Given the root of a binary tree, return the lowest common ancestor of its deepest leaves. + +Recall that: + +The node of a binary tree is a leaf if and only if it has no children +The depth of the root of the tree is 0. if the depth of a node is d, the depth of each of its children is d + 1. +The lowest common ancestor of a set S of nodes, is the node A with the largest depth such that every node in S is in the subtree with root A. + + +Example 1: + + +Input: root = [3,5,1,6,2,0,8,null,null,7,4] +Output: [2,7,4] +Explanation: We return the node with value 2, colored in yellow in the diagram. +The nodes coloured in blue are the deepest leaf-nodes of the tree. +Note that nodes 6, 0, and 8 are also leaf nodes, but the depth of them is 2, but the depth of nodes 7 and 4 is 3. +Example 2: + +Input: root = [1] +Output: [1] +Explanation: The root is the deepest node in the tree, and it's the lca of itself. +Example 3: + +Input: root = [0,1,3,null,2] +Output: [2] +Explanation: The deepest leaf node in the tree is 2, the lca of one node is itself. + + +Constraints: + +The number of nodes in the tree will be in the range [1, 1000]. +0 <= Node.val <= 1000 +The values of the nodes in the tree are unique. +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def lcaDeepestLeaves(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + """ + know the depth, then walk backward + """ + self.maxdepth = 0 + self.deepest_leaves = set() + self.depth(root, 0) + + self.ancestor = None + self.lca(root) + + return self.ancestor + + def lca(self, node): + """ + return number of node found of the targets + """ + if not node: + return 0 + + l = self.lca(node.left) + r = self.lca(node.right) + m = 1 if node in self.deepest_leaves else 0 + ret = l + r + m + if ret == len(self.deepest_leaves) and not self.ancestor: + # only keep the lowest + self.ancestor = node + return ret + + def depth(self, node, d): + if not node: + return + + if d > self.maxdepth: + self.deepest_leaves = set() + self.maxdepth = d + self.deepest_leaves.add(node) + + elif d == self.maxdepth: + self.deepest_leaves.add(node) + + self.depth(node.left, d+1) + self.depth(node.right, d+1) diff --git a/1161 Maximum Level Sum of a Binary Tree.py b/1161 Maximum Level Sum of a Binary Tree.py new file mode 100644 index 0000000..98ecf2a --- /dev/null +++ b/1161 Maximum Level Sum of a Binary Tree.py @@ -0,0 +1,66 @@ +""" +Given the root of a binary tree, the level of its root is 1, the level of its children is 2, and so on. + +Return the smallest level x such that the sum of all the values of nodes at level x is maximal. + + + +Example 1: + + +Input: root = [1,7,0,7,-8,null,null] +Output: 2 +Explanation: +Level 1 sum = 1. +Level 2 sum = 7 + 0 = 7. +Level 3 sum = 7 + -8 = -1. +So we return the level with the maximum sum which is level 2. +Example 2: + +Input: root = [989,null,10250,98693,-89388,null,null,null,-32127] +Output: 2 + + +Constraints: + +The number of nodes in the tree is in the range [1, 10^4]. +-10^5 <= Node.val <= 10^5 +""" +import sys + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def maxLevelSum(self, root: Optional[TreeNode]) -> int: + """ + bfs + """ + level = 1 + q = [root] + maxa = -sys.maxsize-1 + maxlevel = 1 + while q: + new_q = [] + cur = 0 + for e in q: + cur += e.val + if e.left: + new_q.append(e.left) + if e.right: + new_q.append(e.right) + + if cur > maxa: + maxlevel = level + maxa = cur + + q = new_q + level += 1 + + return maxlevel diff --git a/1261 Find Elements in a Contaminated Binary Tree.py b/1261 Find Elements in a Contaminated Binary Tree.py new file mode 100644 index 0000000..ef879a8 --- /dev/null +++ b/1261 Find Elements in a Contaminated Binary Tree.py @@ -0,0 +1,107 @@ +""" +Given a binary tree with the following rules: + +root.val == 0 +For any treeNode: +If treeNode.val has a value x and treeNode.left != null, then treeNode.left.val == 2 * x + 1 +If treeNode.val has a value x and treeNode.right != null, then treeNode.right.val == 2 * x + 2 +Now the binary tree is contaminated, which means all treeNode.val have been changed to -1. + +Implement the FindElements class: + +FindElements(TreeNode* root) Initializes the object with a contaminated binary tree and recovers it. +bool find(int target) Returns true if the target value exists in the recovered binary tree. + + +Example 1: + + +Input +["FindElements","find","find"] +[[[-1,null,-1]],[1],[2]] +Output +[null,false,true] +Explanation +FindElements findElements = new FindElements([-1,null,-1]); +findElements.find(1); // return False +findElements.find(2); // return True +Example 2: + + +Input +["FindElements","find","find","find"] +[[[-1,-1,-1,-1,-1]],[1],[3],[5]] +Output +[null,true,true,false] +Explanation +FindElements findElements = new FindElements([-1,-1,-1,-1,-1]); +findElements.find(1); // return True +findElements.find(3); // return True +findElements.find(5); // return False +Example 3: + + +Input +["FindElements","find","find","find","find"] +[[[-1,null,-1,-1,null,-1]],[2],[3],[4],[5]] +Output +[null,true,false,false,true] +Explanation +FindElements findElements = new FindElements([-1,null,-1,-1,null,-1]); +findElements.find(2); // return True +findElements.find(3); // return False +findElements.find(4); // return False +findElements.find(5); // return True + + +Constraints: + +TreeNode.val == -1 +The height of the binary tree is less than or equal to 20 +The total number of nodes is between [1, 10^4] +Total calls of find() is between [1, 10^4] +0 <= target <= 10^6 +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class FindElements: + def __init__(self, root: Optional[TreeNode]): + self.root = root + self.dfs(root, 0) + + def dfs(self, cur, val): + if not cur: + return + + cur.val = val + self.dfs(cur.left, 2*val+1) + self.dfs(cur.right, 2*val+2) + + def find(self, target: int) -> bool: + return self.s(self.root, target) + + def s(self, node, target): + if not node: + return False + if node.val == target: + return True + if node.val > target: + return False + + if self.s(node.left, target): + return True + if self.s(node.right, target): + return True + + return False + + +# Your FindElements object will be instantiated and called as such: +# obj = FindElements(root) +# param_1 = obj.find(target) \ No newline at end of file diff --git a/1302 Deepest Leaves Sum.py b/1302 Deepest Leaves Sum.py new file mode 100644 index 0000000..2ab897a --- /dev/null +++ b/1302 Deepest Leaves Sum.py @@ -0,0 +1,52 @@ +""" +Given the root of a binary tree, return the sum of values of its deepest leaves. + + +Example 1: + + +Input: root = [1,2,3,4,5,null,6,7,null,null,null,null,8] +Output: 15 +Example 2: + +Input: root = [6,7,8,2,7,1,3,9,null,1,4,null,null,null,5] +Output: 19 + + +Constraints: + +The number of nodes in the tree is in the range [1, 10^4]. +1 <= Node.val <= 100 +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def deepestLeavesSum(self, root: Optional[TreeNode]) -> int: + """ + dfs + """ + self.ret = 0 + self.maxdepth = 0 + self.dfs(root, 0) + return self.ret + + def dfs(self, cur, d): + if not cur: + return + + if d == self.maxdepth: + self.ret += cur.val + elif d > self.maxdepth: + self.maxdepth = d + self.ret = cur.val + + self.dfs(cur.left, d+1) + self.dfs(cur.right, d+1) + + diff --git a/1305 All Elements in Two Binary Search Trees.py b/1305 All Elements in Two Binary Search Trees.py new file mode 100644 index 0000000..86f631b --- /dev/null +++ b/1305 All Elements in Two Binary Search Trees.py @@ -0,0 +1,76 @@ +""" +Given two binary search trees root1 and root2, return a list containing all the integers from both trees sorted in ascending order. + + + +Example 1: + + +Input: root1 = [2,1,4], root2 = [1,0,3] +Output: [0,1,1,2,3,4] +Example 2: + + +Input: root1 = [1,null,8], root2 = [8,1] +Output: [1,1,8,8] + + +Constraints: + +The number of nodes in each tree is in the range [0, 5000]. +-10^5 <= Node.val <= 10^5 +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def getAllElements(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> List[int]: + """ + maintain two lists and + like merge sort, morris traversal? + """ + gen1 = self.morris(root1) + gen2 = self.morris(root2) + ret = [] + a = next(gen1, None) + b = next(gen2, None) + # None is different from 0 + while a is not None or b is not None: + if a is not None and b is not None: + if a > b: + ret.append(b) + b = next(gen2, None) + else: + ret.append(a) + a = next(gen1, None) + elif a is not None: + ret.append(a) + a = next(gen1, None) + else: + ret.append(b) + b = next(gen2, None) + + return ret + + def morris(self, cur): + while cur: + if not cur.left: + yield cur.val + cur = cur.right + else: + pre = cur.left + while pre.right and pre.right != cur: + pre = pre.right + + if not pre.right: + pre.right = cur + cur = cur.left + else: + pre.right = None + yield cur.val + cur = cur.right diff --git a/1315 Sum of Nodes with Even-Valued Grandparent.py b/1315 Sum of Nodes with Even-Valued Grandparent.py new file mode 100644 index 0000000..d14e823 --- /dev/null +++ b/1315 Sum of Nodes with Even-Valued Grandparent.py @@ -0,0 +1,64 @@ +""" +Given the root of a binary tree, return the sum of values of nodes with an even-valued grandparent. If there are no nodes with an even-valued grandparent, return 0. + +A grandparent of a node is the parent of its parent if it exists. + + + +Example 1: + + +Input: root = [6,7,8,2,7,1,3,9,null,1,4,null,null,null,5] +Output: 18 +Explanation: The red nodes are the nodes with even-value grandparent while the blue nodes are the even-value grandparents. +Example 2: + + +Input: root = [1] +Output: 0 + + +Constraints: + +The number of nodes in the tree is in the range [1, 10^4]. +1 <= Node.val <= 100 +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def sumEvenGrandparent(self, root: Optional[TreeNode]) -> int: + """ + 1. maintain a predecessor pointer + 2. dfs + """ + self.ret = 0 + self.dfs(root) + return self.ret + + def dfs(self, node): + if not node: + return + + if node.val % 2 == 0: + self.plus(node, 0) + + self.dfs(node.left) + self.dfs(node.right) + + def plus(self, node, d): + if not node: + return + + if d == 2: + self.ret += node.val + return + + self.plus(node.left, d+1) + self.plus(node.right, d+1) + diff --git a/1325 Delete Leaves With a Given Value.py b/1325 Delete Leaves With a Given Value.py new file mode 100644 index 0000000..f32511a --- /dev/null +++ b/1325 Delete Leaves With a Given Value.py @@ -0,0 +1,68 @@ +""" +Given a binary tree root and an integer target, delete all the leaf nodes with value target. + +Note that once you delete a leaf node with value target, if its parent node becomes a leaf node and has the value target, it should also be deleted (you need to continue doing that until you cannot). + + + +Example 1: + + + +Input: root = [1,2,3,2,null,2,4], target = 2 +Output: [1,null,3,null,4] +Explanation: Leaf nodes in green with value (target = 2) are removed (Picture in left). +After removing, new nodes become leaf nodes with value (target = 2) (Picture in center). +Example 2: + + + +Input: root = [1,3,3,3,2], target = 3 +Output: [1,3,null,null,2] +Example 3: + + + +Input: root = [1,2,null,2,null,2], target = 2 +Output: [1] +Explanation: Leaf nodes in green with value (target = 2) are removed at each step. + + +Constraints: + +The number of nodes in the tree is in the range [1, 3000]. +1 <= Node.val, target <= 1000 +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]: + """ + dfs + """ + dummy = TreeNode() + dummy.left = root + self.dfs(dummy, True, root, target) + return dummy.left + + def dfs(self, pi, is_left, cur, target): + if not cur: + return + + # delete children first, post-order traversal + self.dfs(cur, True, cur.left, target) + self.dfs(cur, False, cur.right, target) + + if not cur.left and not cur.right and cur.val == target: + if is_left: + pi.left = None + else: + pi.right = None + + \ No newline at end of file diff --git a/1448 Count Good Nodes in Binary Tree.py b/1448 Count Good Nodes in Binary Tree.py new file mode 100644 index 0000000..5cd3069 --- /dev/null +++ b/1448 Count Good Nodes in Binary Tree.py @@ -0,0 +1,70 @@ +""" +Given a binary tree root, a node X in the tree is named good if in the path from root to X there are no nodes with a value greater than X. + +Return the number of good nodes in the binary tree. + + + +Example 1: + + + +Input: root = [3,1,4,3,null,1,5] +Output: 4 +Explanation: Nodes in blue are good. +Root Node (3) is always a good node. +Node 4 -> (3,4) is the maximum value in the path starting from the root. +Node 5 -> (3,4,5) is the maximum value in the path +Node 3 -> (3,1,3) is the maximum value in the path. +Example 2: + + + +Input: root = [3,3,null,4,2] +Output: 3 +Explanation: Node 2 -> (3, 3, 2) is not good, because "3" is higher than it. +Example 3: + +Input: root = [1] +Output: 1 +Explanation: Root is considered as good. + + +Constraints: + +The number of nodes in the binary tree is in the range [1, 10^5]. +Each node's value is between [-10^4, 10^4]. +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def goodNodes(self, root: TreeNode) -> int: + """ + dfs, maintain a path + check the max(path) against the node + monotonic stack to efficienet find max(path)? + mono asc stack O(1) + """ + self.ret = 0 + self.dfs(root, []) + return self.ret + + def dfs(self, node, stk): + if not node: + return + + if not stk or stk[~0] <= node.val: + stk.append(node.val) + self.ret += 1 + + self.dfs(node.left, stk) + self.dfs(node.right, stk) + + if stk and stk[~0] == node.val: + stk.pop() diff --git a/2487 Remove Nodes From Linked List.py b/2487 Remove Nodes From Linked List.py new file mode 100644 index 0000000..c2f3adc --- /dev/null +++ b/2487 Remove Nodes From Linked List.py @@ -0,0 +1,55 @@ +""" +You are given the head of a linked list. + +Remove every node which has a node with a greater value anywhere to the right side of it. + +Return the head of the modified linked list. + +Example 1: + +Input: head = [5,2,13,3,8] +Output: [13,8] +Explanation: The nodes that should be removed are 5, 2 and 3. +- Node 13 is to the right of node 5. +- Node 13 is to the right of node 2. +- Node 8 is to the right of node 3. +Example 2: + +Input: head = [1,1,1,1] +Output: [1,1,1,1] +Explanation: Every node has value 1, so no nodes are removed. + + +Constraints: + +The number of the nodes in the given list is in the range [1, 10^5]. +1 <= Node.val <= 10^5 +""" +# Definition for singly-linked list. +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + + +class Solution: + def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]: + """ + monotonically decreasing stack to figure out node to remove + stack keep track of the previous/predecessor of the node to be removed + """ + dummy = ListNode(None, head) + pre = dummy + stk = [] + while pre.next: + cur = pre.next + while stk and stk[-1].next.val < cur.val: + pi = stk.pop() + pi.next = cur + pre = pi + + stk.append(pre) + pre = cur + + return dummy.next + diff --git a/2865 Beautiful Towers I.py b/2865 Beautiful Towers I.py new file mode 100644 index 0000000..f662cca --- /dev/null +++ b/2865 Beautiful Towers I.py @@ -0,0 +1,119 @@ +""" +You are given an array heights of n integers representing the number of bricks in n consecutive towers. Your task is to remove some bricks to form a mountain-shaped tower arrangement. In this arrangement, the tower heights are non-decreasing, reaching a maximum peak value with one or multiple consecutive towers and then non-increasing. + +Return the maximum possible sum of heights of a mountain-shaped tower arrangement. + + + +Example 1: + +Input: heights = [5,3,4,1,1] + +Output: 13 + +Explanation: + +We remove some bricks to make heights = [5,3,3,1,1], the peak is at index 0. + +Example 2: + +Input: heights = [6,5,3,9,2,7] + +Output: 22 + +Explanation: + +We remove some bricks to make heights = [3,3,3,9,2,2], the peak is at index 3. + +Example 3: + +Input: heights = [3,2,5,5,2,3] + +Output: 18 + +Explanation: + +We remove some bricks to make heights = [2,2,5,5,2,2], the peak is at index 2 or 3. + + + +Constraints: + +1 <= n == heights.length <= 10^3 +1 <= heights[i] <= 10^9 +""" +class Solution: + def maximumSumOfHeights_brute(self, H: List[int]) -> int: + """ + Select a peak + then make mountain-shaped using stack + + Get the heighest + O(N^2) + """ + maxa = 0 + N = len(H) + for p in range(N): # peak + L_stk = [p] + for i in range(p-1, -1, -1): + if H[i] > H[L_stk[-1]]: + L_stk.append(L_stk[-1]) + else: + L_stk.append(i) + R_stk = [p] + for i in range(p+1, N): + if H[i] > H[R_stk[-1]]: + R_stk.append(R_stk[-1]) + else: + R_stk.append(i) + cur = sum(H[i] for i in L_stk +R_stk) - H[p] + maxa = max(maxa, cur) + + return maxa + + def maximumSumOfHeights(self, H: List[int]) -> int: + """ + O(N) + Consider 1 side of the problem, from left to peak + Let L[i] be the max sum of upward-shaped area ended at index i + Maintain a monotonically increasing stack + A[lo] < A[mid] > A[i] + A[mid] is the local min for (lo, mid] + when popping mid: + area -= (mid - lo) * A[mid] + # note: not area -= (i - (lo+1)) * A[mid] + # for mid as local min as (lo, i) + # since mid has not impact on (mid, i) + A[i] is the new local min for (lo, i] + area += (i - lo) * A[i] + + same for R when scanning from right to peak + """ + L = self.dp(H) + R = self.dp(H[::-1]) + maxa = 0 + for i in range(len(H)): + cur = L[i] + R[~i] - H[i] # double count H[i] + maxa = max(maxa, cur) + + return maxa + + def dp(self, H): + N = len(H) + L = [0 for _ in range(N)] + stk = [] # mono asc + H.append(-sys.maxsize-1) + cur = 0 + for i in range(N): + while stk and H[stk[-1]] > H[i]: + mid = stk.pop() + lo = stk[-1] if stk else -1 + cur -= (mid - lo) * H[mid] + + lo = stk[-1] if stk else -1 + cur += (i-lo) * H[i] # (lo, i] + L[i] = cur + stk.append(i) + + H.pop() + return L \ No newline at end of file diff --git a/2866 Beautiful Towers II.py b/2866 Beautiful Towers II.py new file mode 100644 index 0000000..2961e62 --- /dev/null +++ b/2866 Beautiful Towers II.py @@ -0,0 +1,79 @@ +""" +You are given a 0-indexed array maxHeights of n integers. + +You are tasked with building n towers in the coordinate line. The ith tower is built at coordinate i and has a height of heights[i]. + +A configuration of towers is beautiful if the following conditions hold: + +1 <= heights[i] <= maxHeights[i] +heights is a mountain array. +Array heights is a mountain if there exists an index i such that: + +For all 0 < j <= i, heights[j - 1] <= heights[j] +For all i <= k < n - 1, heights[k + 1] <= heights[k] +Return the maximum possible sum of heights of a beautiful configuration of towers. + + +Example 1: + +Input: maxHeights = [5,3,4,1,1] +Output: 13 +Explanation: One beautiful configuration with a maximum sum is heights = [5,3,3,1,1]. This configuration is beautiful since: +- 1 <= heights[i] <= maxHeights[i] +- heights is a mountain of peak i = 0. +It can be shown that there exists no other beautiful configuration with a sum of heights greater than 13. +Example 2: + +Input: maxHeights = [6,5,3,9,2,7] +Output: 22 +Explanation: One beautiful configuration with a maximum sum is heights = [3,3,3,9,2,2]. This configuration is beautiful since: +- 1 <= heights[i] <= maxHeights[i] +- heights is a mountain of peak i = 3. +It can be shown that there exists no other beautiful configuration with a sum of heights greater than 22. +Example 3: + +Input: maxHeights = [3,2,5,5,2,3] +Output: 18 +Explanation: One beautiful configuration with a maximum sum is heights = [2,2,5,5,2,2]. This configuration is beautiful since: +- 1 <= heights[i] <= maxHeights[i] +- heights is a mountain of peak i = 2. +Note that, for this configuration, i = 3 can also be considered a peak. +It can be shown that there exists no other beautiful configuration with a sum of heights greater than 18. + + +Constraints: + +1 <= n == maxHeights.length <= 10^5 +1 <= maxHeights[i] <= 10^9 +""" +class Solution: + def maximumSumOfHeights(self, H: List[int]) -> int: + """ + Let L[i] be the largest asc slide for H[:i] + Let R[i] be the largest desc slide for H[i:] + """ + N = len(H) + L = self.dp(H) + R = self.dp(H[::-1])[::-1] + maxa = 0 + for i in range(N+1): + cur = L[i] + R[i] + maxa = max(maxa, cur) + + return maxa + + def dp(self, H): + N = len(H) + L = [0 for _ in range(N+1)] + stk = [] # mono asc + H.append(-sys.maxsize-1) + for i in range(N): + while stk and H[stk[-1]] > H[i]: + mid = stk.pop() + + lo = stk[-1] if stk else -1 + L[i+1] = L[lo+1] + (i-lo) * H[i] + stk.append(i) + + H.pop() + return L \ No newline at end of file diff --git a/655 Print Binary Tree.py b/655 Print Binary Tree.py new file mode 100644 index 0000000..e1a6d82 --- /dev/null +++ b/655 Print Binary Tree.py @@ -0,0 +1,74 @@ +""" +Given the root of a binary tree, construct a 0-indexed m x n string matrix res that represents a formatted layout of the tree. The formatted layout matrix should be constructed using the following rules: + +The height of the tree is height and the number of rows m should be equal to height + 1. +The number of columns n should be equal to 2^(height+1) - 1. +Place the root node in the middle of the top row (more formally, at location res[0][(n-1)/2]). +For each node that has been placed in the matrix at position res[r][c], place its left child at res[r+1][c-2^(height-r-1)] and its right child at res[r+1][c+2^(height-r-1)]. +Continue this process until all the nodes in the tree have been placed. +Any empty cells should contain the empty string "". +Return the constructed matrix res. + +Example 1: + +Input: root = [1,2] +Output: +[["","1",""], + ["2","",""]] +Example 2: + + +Input: root = [1,2,3,null,4] +Output: +[["","","","1","","",""], + ["","2","","","","3",""], + ["","","4","","","",""]] + + +Constraints: + +The number of nodes in the tree is in the range [1, 2^10]. +-99 <= Node.val <= 99 +The depth of the tree will be in the range [1, 10]. +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def printTree(self, root: Optional[TreeNode]) -> List[List[str]]: + """ + get height first + then brute force + """ + h = -1 + q = [root] + while q: + new_q = [] + for node in q: + if node.left: + new_q.append(node.left) + if node.right: + new_q.append(node.right) + q = new_q + h += 1 + + M = h + 1 + N = (1 << h + 1) - 1 + res = [ + ["" for _ in range(N)] + for _ in range(M) + ] + self.dfs(h, res, 0, (N-1)//2, root) + return res + + def dfs(self, h, res, r, c, node): + res[r][c] = str(node.val) + if node.left: + self.dfs(h, res, r+1, c - (1 << h-r-1), node.left) + if node.right: + self.dfs(h, res, r+1, c + (1 << h-r-1), node.right) diff --git a/690 Employee Importance.py b/690 Employee Importance.py new file mode 100644 index 0000000..4d97278 --- /dev/null +++ b/690 Employee Importance.py @@ -0,0 +1,66 @@ +""" +You have a data structure of employee information, including the employee's unique ID, importance value, and direct subordinates' IDs. + +You are given an array of employees employees where: + +employees[i].id is the ID of the ith employee. +employees[i].importance is the importance value of the ith employee. +employees[i].subordinates is a list of the IDs of the direct subordinates of the ith employee. +Given an integer id that represents an employee's ID, return the total importance value of this employee and all their direct and indirect subordinates. + + + +Example 1: + + +Input: employees = [[1,5,[2,3]],[2,3,[]],[3,3,[]]], id = 1 +Output: 11 +Explanation: Employee 1 has an importance value of 5 and has two direct subordinates: employee 2 and employee 3. +They both have an importance value of 3. +Thus, the total importance value of employee 1 is 5 + 3 + 3 = 11. +Example 2: + + +Input: employees = [[1,2,[5]],[5,-3,[]]], id = 5 +Output: -3 +Explanation: Employee 5 has an importance value of -3 and has no direct subordinates. +Thus, the total importance value of employee 5 is -3. + + +Constraints: + +1 <= employees.length <= 2000 +1 <= employees[i].id <= 2000 +All employees[i].id are unique. +-100 <= employees[i].importance <= 100 +One employee has at most one direct leader and may have several subordinates. +The IDs in employees[i].subordinates are valid IDs. +""" +""" +# Definition for Employee. +class Employee: + def __init__(self, id: int, importance: int, subordinates: List[int]): + self.id = id + self.importance = importance + self.subordinates = subordinates +""" + +class Solution: + def getImportance(self, employees: List['Employee'], id: int) -> int: + """ + find it then dfs + """ + G = {} + for e in employees: + G[e.id] = e + + for e in employees: + if e.id == id: + return self.dfs(e, G) + + def dfs(self, node, G): + s = node.importance + for i in node.subordinates: + child = G[i] + s += self.dfs(child, G) + return s From 3ab6ebf9d5ea55858f40bfe608674592105c4374 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 22 Mar 2025 21:03:13 -0400 Subject: [PATCH 340/344] tree --- 1372 Longest ZigZag Path in a Binary Tree.py | 105 ++++++++++++++++ 1376 Time Needed to Inform All Employees.py | 61 +++++++++ 1382 Balance a Binary Search Tree.py | 61 +++++++++ ...um Time to Collect All Apples in a Tree.py | 77 ++++++++++++ ...eudo-Palindromic Paths in a Binary Tree.py | 117 ++++++++++++++++++ 1609 Even Odd Tree.py | 84 +++++++++++++ 2196 Create Binary Tree From Descriptions.py | 69 +++++++++++ ...Count Nodes Equal to Average of Subtree.py | 65 ++++++++++ 2368 Reachable Nodes With Restrictions.py | 64 ++++++++++ 2415 Reverse Odd Levels of Binary Tree.py | 96 ++++++++++++++ ...erations to Sort a Binary Tree by Level.py | 95 ++++++++++++++ 11 files changed, 894 insertions(+) create mode 100644 1372 Longest ZigZag Path in a Binary Tree.py create mode 100644 1376 Time Needed to Inform All Employees.py create mode 100644 1382 Balance a Binary Search Tree.py create mode 100644 1443 Minimum Time to Collect All Apples in a Tree.py create mode 100644 1457 Pseudo-Palindromic Paths in a Binary Tree.py create mode 100644 1609 Even Odd Tree.py create mode 100644 2196 Create Binary Tree From Descriptions.py create mode 100644 2265 Count Nodes Equal to Average of Subtree.py create mode 100644 2368 Reachable Nodes With Restrictions.py create mode 100644 2415 Reverse Odd Levels of Binary Tree.py create mode 100644 2471 Minimum Number of Operations to Sort a Binary Tree by Level.py diff --git a/1372 Longest ZigZag Path in a Binary Tree.py b/1372 Longest ZigZag Path in a Binary Tree.py new file mode 100644 index 0000000..f4bf58b --- /dev/null +++ b/1372 Longest ZigZag Path in a Binary Tree.py @@ -0,0 +1,105 @@ +""" +You are given the root of a binary tree. + +A ZigZag path for a binary tree is defined as follow: + +Choose any node in the binary tree and a direction (right or left). +If the current direction is right, move to the right child of the current node; otherwise, move to the left child. +Change the direction from right to left or from left to right. +Repeat the second and third steps until you can't move in the tree. +Zigzag length is defined as the number of nodes visited - 1. (A single node has a length of 0). + +Return the longest ZigZag path contained in that tree. + + + +Example 1: + + +Input: root = [1,null,1,1,1,null,null,1,1,null,1,null,null,null,1] +Output: 3 +Explanation: Longest ZigZag path in blue nodes (right -> left -> right). +Example 2: + + +Input: root = [1,1,1,null,1,null,null,1,1,null,1] +Output: 4 +Explanation: Longest ZigZag path in blue nodes (left -> right -> left -> right). +Example 3: + +Input: root = [1] +Output: 0 + + +Constraints: + +The number of nodes in the tree is in the range [1, 5 * 10^4]. +1 <= Node.val <= 100 +""" +import functools + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class SolutionN2: + def longestZigZag(self, root: Optional[TreeNode]) -> int: + """ + Brute force visit every node, choose left or right, O(N^2) + + Memoization: O(N) + """ + self.maxa = 0 + self.visit(root) + return self.maxa + + @functools.lru_cache(maxsize=None) + def dfs(self, node, is_left) -> int: + if not node: + return 0 + + if is_left: + l = self.dfs(node.right, False) + else: + l = self.dfs(node.left, True) + + ret = l + 1 + self.maxa = max(self.maxa, ret-1) + return ret + + def visit(self, node): + if not node: + return + + self.dfs(node, True) + self.dfs(node, False) + self.visit(node.left) + self.visit(node.right) + + +class Solution: + def longestZigZag(self, root: Optional[TreeNode]) -> int: + self.maxa = 0 + self.dfs(root, True, 0) + self.dfs(root, False, 0) # redundant + return self.maxa - 1 + + def dfs(self, node, is_left, l): + if not node: + return + + l += 1 + self.maxa = max(self.maxa, l) + + if is_left: + self.dfs(node.right, False, l) + # restart from node + self.dfs(node.left, True, 1) + else: + self.dfs(node.left, True, l) + # restart from node + self.dfs(node.right, False, 1) \ No newline at end of file diff --git a/1376 Time Needed to Inform All Employees.py b/1376 Time Needed to Inform All Employees.py new file mode 100644 index 0000000..fd9bb8f --- /dev/null +++ b/1376 Time Needed to Inform All Employees.py @@ -0,0 +1,61 @@ +""" +A company has n employees with a unique ID for each employee from 0 to n - 1. The head of the company is the one with headID. + +Each employee has one direct manager given in the manager array where manager[i] is the direct manager of the i-th employee, manager[headID] = -1. Also, it is guaranteed that the subordination relationships have a tree structure. + +The head of the company wants to inform all the company employees of an urgent piece of news. He will inform his direct subordinates, and they will inform their subordinates, and so on until all employees know about the urgent news. + +The i-th employee needs informTime[i] minutes to inform all of his direct subordinates (i.e., After informTime[i] minutes, all his direct subordinates can start spreading the news). + +Return the number of minutes needed to inform all the employees about the urgent news. + + + +Example 1: + +Input: n = 1, headID = 0, manager = [-1], informTime = [0] +Output: 0 +Explanation: The head of the company is the only employee in the company. +Example 2: + + +Input: n = 6, headID = 2, manager = [2,2,-1,2,2,2], informTime = [0,0,1,0,0,0] +Output: 1 +Explanation: The head of the company with id = 2 is the direct manager of all the employees in the company and needs 1 minute to inform them all. +The tree structure of the employees in the company is shown. + + +Constraints: + +1 <= n <= 10^5 +0 <= headID < n +manager.length == n +0 <= manager[i] < n +manager[headID] == -1 +informTime.length == n d +0 <= informTime[i] <= d +informTime[i] == 0 if employee i has no subordinates. +It is guaranteed that all the employees can be informed. +""" +from collections import defaultdict + + +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + """ + it is a graph problem + """ + G = defaultdict(list) + time = defaultdict(int) + + for i, pi in enumerate(manager): + G[pi].append(i) + + self.dfs(G, time, headID, informTime) + return max(time.values()) + + def dfs(self, G, time, cur, inform_time): + t = time[cur] + inform_time[cur] + for nbr in G[cur]: + time[nbr] = t + self.dfs(G, time, nbr, inform_time) diff --git a/1382 Balance a Binary Search Tree.py b/1382 Balance a Binary Search Tree.py new file mode 100644 index 0000000..ae31878 --- /dev/null +++ b/1382 Balance a Binary Search Tree.py @@ -0,0 +1,61 @@ +""" +Given the root of a binary search tree, return a balanced binary search tree with the same node values. If there is more than one answer, return any of them. + +A binary search tree is balanced if the depth of the two subtrees of every node never differs by more than 1. + + + +Example 1: + + +Input: root = [1,null,2,null,3,null,4,null,null] +Output: [2,1,3,null,null,null,4] +Explanation: This is not the only correct answer, [3,1,4,null,2] is also correct. +Example 2: + + +Input: root = [2,1,3] +Output: [2,1,3] + + +Constraints: + +The number of nodes in the tree is in the range [1, 10^4]. +1 <= Node.val <= 10^5 +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def balanceBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + """ + To find the root: middle point of the array + To construct balance binary search tree: recurisively + """ + self.A = [] + self.to_array(root) + return self.balance(self.A, 0, len(self.A)) + + def to_array(self, node): + if not node: + return + + self.to_array(node.left) + self.A.append(node.val) + self.to_array(node.right) + + def balance(self, A, lo, hi): + # [lo, hi) + if lo >= hi: + return None + + mid = (lo + hi) // 2 + left = self.balance(A, lo, mid) + right = self.balance(A, mid+1, hi) + node = TreeNode(A[mid], left, right) + return node \ No newline at end of file diff --git a/1443 Minimum Time to Collect All Apples in a Tree.py b/1443 Minimum Time to Collect All Apples in a Tree.py new file mode 100644 index 0000000..3955456 --- /dev/null +++ b/1443 Minimum Time to Collect All Apples in a Tree.py @@ -0,0 +1,77 @@ +""" +Given an undirected tree consisting of n vertices numbered from 0 to n-1, which has some apples in their vertices. You spend 1 second to walk over one edge of the tree. Return the minimum time in seconds you have to spend to collect all apples in the tree, starting at vertex 0 and coming back to this vertex. + +The edges of the undirected tree are given in the array edges, where edges[i] = [ai, bi] means that exists an edge connecting the vertices ai and bi. Additionally, there is a boolean array hasApple, where hasApple[i] = true means that vertex i has an apple; otherwise, it does not have any apple. + + + +Example 1: + + +Input: n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,true,false,true,true,false] +Output: 8 +Explanation: The figure above represents the given tree where red vertices have an apple. One optimal path to collect all apples is shown by the green arrows. +Example 2: + + +Input: n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,true,false,false,true,false] +Output: 6 +Explanation: The figure above represents the given tree where red vertices have an apple. One optimal path to collect all apples is shown by the green arrows. +Example 3: + +Input: n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], hasApple = [false,false,false,false,false,false,false] +Output: 0 + + +Constraints: + +1 <= n <= 10^5 +edges.length == n - 1 +edges[i].length == 2 +0 <= ai < bi <= n - 1 +hasApple.length == n +""" +from collections import defaultdict + + +class Solution: + def minTime(self, n: int, edges: List[List[int]], hasApple: List[bool]) -> int: + """ + first DFS, construct a map of has_apple in the subtree + second DFS, count time + """ + G = defaultdict(list) + for u, v in edges: + G[u].append(v) + G[v].append(u) + + self.has_apple = hasApple + self.contain = defaultdict(bool) + self.contain_apple(G, 0, defaultdict(bool)) + + self.ret = 0 + if 0 in self.contain: + self.dfs(G, 0, defaultdict(bool)) + + return max(0, self.ret - 2) + + def contain_apple(self, G, node, visited): + visited[node] = True + contain_apple = self.has_apple[node] + for nbr in G[node]: + if not visited[nbr]: + contain_apple |= self.contain_apple(G, nbr, visited) + + if contain_apple: + self.contain[node] = True + + return contain_apple + + def dfs(self, G, node, visited): + visited[node] = True + self.ret += 1 + for nbr in G[node]: + if not visited[nbr] and self.contain[nbr]: + self.dfs(G, nbr, visited) + + self.ret += 1 \ No newline at end of file diff --git a/1457 Pseudo-Palindromic Paths in a Binary Tree.py b/1457 Pseudo-Palindromic Paths in a Binary Tree.py new file mode 100644 index 0000000..fa50e03 --- /dev/null +++ b/1457 Pseudo-Palindromic Paths in a Binary Tree.py @@ -0,0 +1,117 @@ +""" +Given a binary tree where node values are digits from 1 to 9. A path in the binary tree is said to be pseudo-palindromic if at least one permutation of the node values in the path is a palindrome. + +Return the number of pseudo-palindromic paths going from the root node to leaf nodes. + + + +Example 1: + + + +Input: root = [2,3,1,3,1,null,1] +Output: 2 +Explanation: The figure above represents the given binary tree. There are three paths going from the root node to leaf nodes: the red path [2,3,3], the green path [2,1,1], and the path [2,3,1]. Among these paths only red path and green path are pseudo-palindromic paths since the red path [2,3,3] can be rearranged in [3,2,3] (palindrome) and the green path [2,1,1] can be rearranged in [1,2,1] (palindrome). +Example 2: + + + +Input: root = [2,1,1,1,3,null,null,null,null,null,1] +Output: 1 +Explanation: The figure above represents the given binary tree. There are three paths going from the root node to leaf nodes: the green path [2,1,1], the path [2,1,3,1], and the path [2,1]. Among these paths only the green path is pseudo-palindromic since [2,1,1] can be rearranged in [1,2,1] (palindrome). +Example 3: + +Input: root = [9] +Output: 1 + + +Constraints: + +The number of nodes in the tree is in the range [1, 10^5]. +1 <= Node.val <= 9 +""" +from collections import defaultdict + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class SolutionTLE: + def pseudoPalindromicPaths (self, root: Optional[TreeNode]) -> int: + """ + DFS + travel to leave + maintain a counter to check for palindrom + """ + self.ret = 0 + self.dfs(root, []) + return self.ret + + def dfs(self, node, stk): + if not node: + return + + stk.append(node.val) + if not node.left and not node.right: + if self.is_palindrom(stk): + self.ret += 1 + + self.dfs(node.left, stk) + self.dfs(node.right, stk) + stk.pop() + + def is_palindrom(self, stk): + counter = defaultdict(int) + for e in stk: + counter[e] += 1 + + has_odd = False + for cnt in counter.values(): + if cnt % 2 == 1: + if has_odd: + return False + else: + has_odd = True + + return True + + +class Solution: + def pseudoPalindromicPaths (self, root: Optional[TreeNode]) -> int: + """ + DFS + travel to leave + maintain a counter to check for palindrom + """ + self.ret = 0 + self.dfs(root, defaultdict(int)) + return self.ret + + def dfs(self, node, counter): + if not node: + return + + counter[node.val] += 1 + if not node.left and not node.right: + if self.is_palindrom(counter): + self.ret += 1 + + self.dfs(node.left, counter) + self.dfs(node.right, counter) + counter[node.val] -= 1 + + def is_palindrom(self, counter): + has_odd = False + for cnt in counter.values(): + if cnt % 2 == 1: + if has_odd: + return False + else: + has_odd = True + + return True \ No newline at end of file diff --git a/1609 Even Odd Tree.py b/1609 Even Odd Tree.py new file mode 100644 index 0000000..cf4f6c7 --- /dev/null +++ b/1609 Even Odd Tree.py @@ -0,0 +1,84 @@ +""" +A binary tree is named Even-Odd if it meets the following conditions: + +The root of the binary tree is at level index 0, its children are at level index 1, their children are at level index 2, etc. +For every even-indexed level, all nodes at the level have odd integer values in strictly increasing order (from left to right). +For every odd-indexed level, all nodes at the level have even integer values in strictly decreasing order (from left to right). +Given the root of a binary tree, return true if the binary tree is Even-Odd, otherwise return false. + + + +Example 1: + + +Input: root = [1,10,4,3,null,7,9,12,8,6,null,null,2] +Output: true +Explanation: The node values on each level are: +Level 0: [1] +Level 1: [10,4] +Level 2: [3,7,9] +Level 3: [12,8,6,2] +Since levels 0 and 2 are all odd and increasing and levels 1 and 3 are all even and decreasing, the tree is Even-Odd. +Example 2: + + +Input: root = [5,4,2,3,3,7] +Output: false +Explanation: The node values on each level are: +Level 0: [5] +Level 1: [4,2] +Level 2: [3,3,7] +Node values in level 2 must be in strictly increasing order, so the tree is not Even-Odd. +Example 3: + + +Input: root = [5,9,1,3,5,7] +Output: false +Explanation: Node values in the level 1 should be even integers. + + +Constraints: + +The number of nodes in the tree is in the range [1, 10^5]. +1 <= Node.val <= 10^6 +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def isEvenOddTree(self, root: Optional[TreeNode]) -> bool: + """ + bfs + """ + q = [root] + l = 0 + while q: + new_q = [] + for i in range(len(q)): + if l % 2 == 0: + if q[i].val % 2 != 1: + return False + if i > 0: + if not q[i-1].val < q[i].val: + return False + else: + if q[i].val % 2 != 0: + return False + if i > 0: + if not q[i-1].val > q[i].val: + return False + + if q[i].left: + new_q.append(q[i].left) + if q[i].right: + new_q.append(q[i].right) + + q = new_q + l += 1 + + return True diff --git a/2196 Create Binary Tree From Descriptions.py b/2196 Create Binary Tree From Descriptions.py new file mode 100644 index 0000000..0073fad --- /dev/null +++ b/2196 Create Binary Tree From Descriptions.py @@ -0,0 +1,69 @@ +""" +You are given a 2D integer array descriptions where descriptions[i] = [parenti, childi, isLefti] indicates that parenti is the parent of childi in a binary tree of unique values. Furthermore, + +If isLefti == 1, then childi is the left child of parenti. +If isLefti == 0, then childi is the right child of parenti. +Construct the binary tree described by descriptions and return its root. + +The test cases will be generated such that the binary tree is valid. + + + +Example 1: + + +Input: descriptions = [[20,15,1],[20,17,0],[50,20,1],[50,80,0],[80,19,1]] +Output: [50,20,80,15,17,19] +Explanation: The root node is the node with value 50 since it has no parent. +The resulting binary tree is shown in the diagram. +Example 2: + + +Input: descriptions = [[1,2,1],[2,3,0],[3,4,1]] +Output: [1,2,null,null,3,4] +Explanation: The root node is the node with value 1 since it has no parent. +The resulting binary tree is shown in the diagram. + + +Constraints: + +1 <= descriptions.length <= 10^4 +descriptions[i].length == 3 +1 <= parenti, childi <= 10^5 +0 <= isLefti <= 1 +The binary tree described by descriptions is valid. +""" +from collections import defaultdict + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def createBinaryTree(self, descriptions: List[List[int]]) -> Optional[TreeNode]: + """ + Given edges, construct a graph: value -> TreeNode + + How to get a root? Maintain has_parent set + """ + G = defaultdict(TreeNode) + has_parent = set() + for val, child_val, is_left in descriptions: + node = G[val] + node.val = val + child = G[child_val] + child.val = child_val + has_parent.add(child_val) + if is_left: + node.left = child + else: + node.right = child + + for val, node in G.items(): + if val not in has_parent: + return node \ No newline at end of file diff --git a/2265 Count Nodes Equal to Average of Subtree.py b/2265 Count Nodes Equal to Average of Subtree.py new file mode 100644 index 0000000..7a90f1e --- /dev/null +++ b/2265 Count Nodes Equal to Average of Subtree.py @@ -0,0 +1,65 @@ +""" +Given the root of a binary tree, return the number of nodes where the value of the node is equal to the average of the values in its subtree. + +Note: + +The average of n elements is the sum of the n elements divided by n and rounded down to the nearest integer. +A subtree of root is a tree consisting of root and all of its descendants. + + +Example 1: + + +Input: root = [4,8,5,0,1,null,6] +Output: 5 +Explanation: +For the node with value 4: The average of its subtree is (4 + 8 + 5 + 0 + 1 + 6) / 6 = 24 / 6 = 4. +For the node with value 5: The average of its subtree is (5 + 6) / 2 = 11 / 2 = 5. +For the node with value 0: The average of its subtree is 0 / 1 = 0. +For the node with value 1: The average of its subtree is 1 / 1 = 1. +For the node with value 6: The average of its subtree is 6 / 1 = 6. +Example 2: + + +Input: root = [1] +Output: 1 +Explanation: For the node with value 1: The average of its subtree is 1 / 1 = 1. + + +Constraints: + +The number of nodes in the tree is in the range [1, 1000]. +0 <= Node.val <= 1000 +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def averageOfSubtree(self, root: TreeNode) -> int: + """ + sum and count + """ + self.ret = 0 + self.dfs(root) + return self.ret + + def dfs(self, node): + if not node: + return 0, 0 + + s = node.val + c = 1 + + ls, lc = self.dfs(node.left) + rs, rc = self.dfs(node.right) + s += ls + rs + c += lc + rc + if s // c == node.val: + self.ret += 1 + + return s, c \ No newline at end of file diff --git a/2368 Reachable Nodes With Restrictions.py b/2368 Reachable Nodes With Restrictions.py new file mode 100644 index 0000000..62bcf47 --- /dev/null +++ b/2368 Reachable Nodes With Restrictions.py @@ -0,0 +1,64 @@ +""" +There is an undirected tree with n nodes labeled from 0 to n - 1 and n - 1 edges. + +You are given a 2D integer array edges of length n - 1 where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the tree. You are also given an integer array restricted which represents restricted nodes. + +Return the maximum number of nodes you can reach from node 0 without visiting a restricted node. + +Note that node 0 will not be a restricted node. + + + +Example 1: + + +Input: n = 7, edges = [[0,1],[1,2],[3,1],[4,0],[0,5],[5,6]], restricted = [4,5] +Output: 4 +Explanation: The diagram above shows the tree. +We have that [0,1,2,3] are the only nodes that can be reached from node 0 without visiting a restricted node. +Example 2: + + +Input: n = 7, edges = [[0,1],[0,2],[0,5],[0,4],[3,2],[6,5]], restricted = [4,2,1] +Output: 3 +Explanation: The diagram above shows the tree. +We have that [0,5,6] are the only nodes that can be reached from node 0 without visiting a restricted node. + + +Constraints: + +2 <= n <= 10^5 +edges.length == n - 1 +edges[i].length == 2 +0 <= ai, bi < n +ai != bi +edges represents a valid tree. +1 <= restricted.length < n +1 <= restricted[i] < n +All the values of restricted are unique. +""" +from collections import defaultdict + + +class Solution: + def reachableNodes(self, n: int, edges: List[List[int]], restricted: List[int]) -> int: + """ + Just DFS + """ + self.res = set(restricted) + G = defaultdict(list) + for u, v in edges: + G[u].append(v) + G[v].append(u) + + self.ret = 0 + self.dfs(G, 0, defaultdict(bool)) + return self.ret + + def dfs(self, G, cur, visited): + visited[cur] = True + self.ret += 1 + for nbr in G[cur]: + if nbr not in self.res and not visited[nbr]: + self.dfs(G, nbr, visited) + diff --git a/2415 Reverse Odd Levels of Binary Tree.py b/2415 Reverse Odd Levels of Binary Tree.py new file mode 100644 index 0000000..b5b4abb --- /dev/null +++ b/2415 Reverse Odd Levels of Binary Tree.py @@ -0,0 +1,96 @@ +""" +Given the root of a perfect binary tree, reverse the node values at each odd level of the tree. + +For example, suppose the node values at level 3 are [2,1,3,4,7,11,29,18], then it should become [18,29,11,7,4,3,1,2]. +Return the root of the reversed tree. + +A binary tree is perfect if all parent nodes have two children and all leaves are on the same level. + +The level of a node is the number of edges along the path between it and the root node. + + + +Example 1: + + +Input: root = [2,3,5,8,13,21,34] +Output: [2,5,3,8,13,21,34] +Explanation: +The tree has only one odd level. +The nodes at level 1 are 3, 5 respectively, which are reversed and become 5, 3. +Example 2: + + +Input: root = [7,13,11] +Output: [7,11,13] +Explanation: +The nodes at level 1 are 13, 11, which are reversed and become 11, 13. +Example 3: + +Input: root = [0,1,2,0,0,0,0,1,1,1,1,2,2,2,2] +Output: [0,2,1,0,0,0,0,2,2,2,2,1,1,1,1] +Explanation: +The odd levels have non-zero values. +The nodes at level 1 were 1, 2, and are 2, 1 after the reversal. +The nodes at level 3 were 1, 1, 1, 1, 2, 2, 2, 2, and are 2, 2, 2, 2, 1, 1, 1, 1 after the reversal. + + +Constraints: + +The number of nodes in the tree is in the range [1, 2^14]. +0 <= Node.val <= 105 +root is a perfect binary tree. +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class SolutionBFS: + def reverseOddLevels(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + """ + BFS + just swap the value + """ + q = [root] + l = 0 + while q: + new_q = [] + for node in q: + if node.left: + new_q.append(node.left) + if node.right: + new_q.append(node.right) + + if l % 2 == 1: + vals = [node.val for node in q] + for node, val in zip(q, vals[::-1]): + node.val = val + + q = new_q + l += 1 + + return root + + +class Solution: + def reverseOddLevels(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + """ + swap left's left with right's right + swap left's right with right's left + """ + self.dfs(root.left, root.right, 1) + return root + + def dfs(self, left, right, l): + if not left or not right: + return + + if l % 2 == 1: + left.val, right.val = right.val, left.val + + self.dfs(left.left, right.right, l + 1) + self.dfs(left.right, right.left, l + 1) \ No newline at end of file diff --git a/2471 Minimum Number of Operations to Sort a Binary Tree by Level.py b/2471 Minimum Number of Operations to Sort a Binary Tree by Level.py new file mode 100644 index 0000000..9934994 --- /dev/null +++ b/2471 Minimum Number of Operations to Sort a Binary Tree by Level.py @@ -0,0 +1,95 @@ +""" +You are given the root of a binary tree with unique values. + +In one operation, you can choose any two nodes at the same level and swap their values. + +Return the minimum number of operations needed to make the values at each level sorted in a strictly increasing order. + +The level of a node is the number of edges along the path between it and the root node. + + + +Example 1: + + +Input: root = [1,4,3,7,6,8,5,null,null,null,null,9,null,10] +Output: 3 +Explanation: +- Swap 4 and 3. The 2nd level becomes [3,4]. +- Swap 7 and 5. The 3rd level becomes [5,6,8,7]. +- Swap 8 and 7. The 3rd level becomes [5,6,7,8]. +We used 3 operations so return 3. +It can be proven that 3 is the minimum number of operations needed. +Example 2: + + +Input: root = [1,3,2,7,6,5,4] +Output: 3 +Explanation: +- Swap 3 and 2. The 2nd level becomes [2,3]. +- Swap 7 and 4. The 3rd level becomes [4,6,5,7]. +- Swap 6 and 5. The 3rd level becomes [4,5,6,7]. +We used 3 operations so return 3. +It can be proven that 3 is the minimum number of operations needed. +Example 3: + + +Input: root = [1,2,3,4,5,6] +Output: 0 +Explanation: Each level is already sorted in increasing order so return 0. + + +Constraints: + +The number of nodes in the tree is in the range [1, 10^5]. +1 <= Node.val <= 10^5 +All the values of the tree are unique. +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def minimumOperations(self, root: Optional[TreeNode]) -> int: + """ + BFS + minimum swap + Greedy swap? + """ + q = [root] + ret = 0 + while q: + ret += self.count(q) + new_q = [] + for cur in q: + if cur.left: + new_q.append(cur.left) + if cur.right: + new_q.append(cur.right) + + q = new_q + + return ret + + def count(self, q): + A = [node.val for node in q] + T = list(A) + T.sort() + hm = {} + for i, a in enumerate(A): + hm[a] = i + + # greedy swap? + cnt = 0 + for i in range(len(A)): + if A[i] != T[i]: + idx = hm[T[i]] + A[i], A[idx] = A[idx], A[i] + hm[A[i]] = i + hm[A[idx]] = idx + cnt += 1 + + return cnt \ No newline at end of file From 109a8acb8cd62ad60b0e67fbf28e565fcb4c61d5 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 24 Mar 2025 00:23:33 -0400 Subject: [PATCH 341/344] update --- 1530 Number of Good Leaf Nodes Pairs.py | 87 +++++++++++++++++ ... of Time for Binary Tree to Be Infected.py | 63 +++++++++++++ ...imum Fuel Cost to Report to the Capital.py | 94 +++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 1530 Number of Good Leaf Nodes Pairs.py create mode 100644 2385 Amount of Time for Binary Tree to Be Infected.py create mode 100644 2477 Minimum Fuel Cost to Report to the Capital.py diff --git a/1530 Number of Good Leaf Nodes Pairs.py b/1530 Number of Good Leaf Nodes Pairs.py new file mode 100644 index 0000000..0e8bcfb --- /dev/null +++ b/1530 Number of Good Leaf Nodes Pairs.py @@ -0,0 +1,87 @@ +""" +You are given the root of a binary tree and an integer distance. A pair of two different leaf nodes of a binary tree is said to be good if the length of the shortest path between them is less than or equal to distance. + +Return the number of good leaf node pairs in the tree. + + +Example 1: + + +Input: root = [1,2,3,null,4], distance = 3 +Output: 1 +Explanation: The leaf nodes of the tree are 3 and 4 and the length of the shortest path between them is 3. This is the only good pair. +Example 2: + + +Input: root = [1,2,3,4,5,6,7], distance = 3 +Output: 2 +Explanation: The good pairs are [4,5] and [6,7] with shortest path = 2. The pair [4,6] is not good because the length of ther shortest path between them is 4. +Example 3: + +Input: root = [7,1,4,6,null,5,3,null,null,null,null,null,2], distance = 3 +Output: 1 +Explanation: The only good pair is [2,5]. + + +Constraints: + +The number of nodes in the tree is in the range [1, 210]. +1 <= Node.val <= 100 +1 <= distance <= 10 +""" +from collections import defaultdict + + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def countPairs(self, root: Optional[TreeNode], distance: int) -> int: + """ + Start DFS from leaf. Create a graph? + + LCA left * right + maintain a counter: distance -> #leaves + """ + self.ret = 0 + self.dist = distance + self.dfs(root) + return self.ret + + def dfs(self, node): + """ + return a map of distance to the cur node + """ + counter = defaultdict(int) + if not node: + return counter + + if not node.left and not node.right: + counter[0] = 1 + return counter + + left = self.dfs(node.left) + right = self.dfs(node.right) + for k, v in left.items(): + for K, V in right.items(): + if (k+1) + (K+1) <= self.dist: + self.ret += v * V + + # merge + for k, v in left.items(): + k += 1 + if k <= self.dist: + counter[k] += v + for k, v in right.items(): + k += 1 + if k <= self.dist: + counter[k] += v + + return counter + + \ No newline at end of file diff --git a/2385 Amount of Time for Binary Tree to Be Infected.py b/2385 Amount of Time for Binary Tree to Be Infected.py new file mode 100644 index 0000000..73830d9 --- /dev/null +++ b/2385 Amount of Time for Binary Tree to Be Infected.py @@ -0,0 +1,63 @@ +""" +You are given the root of a binary tree with unique values, and an integer start. At minute 0, an infection starts from the node with value start. + +Each minute, a node becomes infected if: + +The node is currently uninfected. +The node is adjacent to an infected node. +Return the number of minutes needed for the entire tree to be infected. + + + +Example 1: + + +Input: root = [1,5,3,null,4,10,6,9,2], start = 3 +Output: 4 +Explanation: The following nodes are infected during: +- Minute 0: Node 3 +- Minute 1: Nodes 1, 10 and 6 +- Minute 2: Node 5 +- Minute 3: Node 4 +- Minute 4: Nodes 9 and 2 +It takes 4 minutes for the whole tree to be infected so we return 4. +Example 2: + + +Input: root = [1], start = 1 +Output: 0 +Explanation: At minute 0, the only node in the tree is infected so we return 0. + + +Constraints: + +The number of nodes in the tree is in the range [1, 10^5]. +1 <= Node.val <= 10^5 +Each node has a unique value. +A node with a value of start exists in the tree. +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def amountOfTime(self, root: Optional[TreeNode], start: int) -> int: + """ + convert to Graph then BFS + + One-pass DFS + * If root is the start => get depth from left and right subtree + * If start is on one (e.g. left) side of the root => depth to the start + right depth + """ + self.maxa = 0 + self.start = start + self.dfs(root) + return self.maxa + + + + \ No newline at end of file diff --git a/2477 Minimum Fuel Cost to Report to the Capital.py b/2477 Minimum Fuel Cost to Report to the Capital.py new file mode 100644 index 0000000..cd2c0a1 --- /dev/null +++ b/2477 Minimum Fuel Cost to Report to the Capital.py @@ -0,0 +1,94 @@ +""" +There is a tree (i.e., a connected, undirected graph with no cycles) structure country network consisting of n cities numbered from 0 to n - 1 and exactly n - 1 roads. The capital city is city 0. You are given a 2D integer array roads where roads[i] = [ai, bi] denotes that there exists a bidirectional road connecting cities ai and bi. + +There is a meeting for the representatives of each city. The meeting is in the capital city. + +There is a car in each city. You are given an integer seats that indicates the number of seats in each car. + +A representative can use the car in their city to travel or change the car and ride with another representative. The cost of traveling between two cities is one liter of fuel. + +Return the minimum number of liters of fuel to reach the capital city. + + + +Example 1: + + +Input: roads = [[0,1],[0,2],[0,3]], seats = 5 +Output: 3 +Explanation: +- Representative1 goes directly to the capital with 1 liter of fuel. +- Representative2 goes directly to the capital with 1 liter of fuel. +- Representative3 goes directly to the capital with 1 liter of fuel. +It costs 3 liters of fuel at minimum. +It can be proven that 3 is the minimum number of liters of fuel needed. +Example 2: + + +Input: roads = [[3,1],[3,2],[1,0],[0,4],[0,5],[4,6]], seats = 2 +Output: 7 +Explanation: +- Representative2 goes directly to city 3 with 1 liter of fuel. +- Representative2 and representative3 go together to city 1 with 1 liter of fuel. +- Representative2 and representative3 go together to the capital with 1 liter of fuel. +- Representative1 goes directly to the capital with 1 liter of fuel. +- Representative5 goes directly to the capital with 1 liter of fuel. +- Representative6 goes directly to city 4 with 1 liter of fuel. +- Representative4 and representative6 go together to the capital with 1 liter of fuel. +It costs 7 liters of fuel at minimum. +It can be proven that 7 is the minimum number of liters of fuel needed. +Example 3: + + +Input: roads = [], seats = 1 +Output: 0 +Explanation: No representatives need to travel to the capital city. + + +Constraints: + +1 <= n <= 10^5 +roads.length == n - 1 +roads[i].length == 2 +0 <= ai, bi < n +ai != bi +roads represents a valid tree. +1 <= seats <= 10^5 +""" +import math +from collections import defaultdict + + +class Solution: + def minimumFuelCost(self, roads: List[List[int]], seats: int) -> int: + """ + accumulate weight on the edge + fuel cost = ceil of edge weight / seats + + dfs returns the sum of weight + """ + self.seats = seats + self.ret = 0 + G = defaultdict(list) + for u, v in roads: + G[u].append(v) + G[v].append(u) + + self.dfs(G, 0, defaultdict(bool)) + return self.ret + + def dfs(self, G, cur, visited): + visited[cur] = True + weight = 0 + for nbr in G[cur]: + if not visited[nbr]: + w = self.dfs(G, nbr, visited) + # cost to come to the current node + self.ret += math.ceil(w / self.seats) + weight += w + + return weight + 1 + + + + From c7cfa39439faa5bdc086d7710e7adcfcb398c468 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 2 Apr 2025 13:29:11 -0400 Subject: [PATCH 342/344] update --- 419 Battleships in a Board.py | 63 +++++++++++++++++++ 529 Minesweeper.py | 3 + 542 01 Matrix.py | 113 ++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 419 Battleships in a Board.py create mode 100644 529 Minesweeper.py create mode 100644 542 01 Matrix.py diff --git a/419 Battleships in a Board.py b/419 Battleships in a Board.py new file mode 100644 index 0000000..6c30b4e --- /dev/null +++ b/419 Battleships in a Board.py @@ -0,0 +1,63 @@ +""" +Given an m x n matrix board where each cell is a battleship 'X' or empty '.', return the number of the battleships on board. + +Battleships can only be placed horizontally or vertically on board. In other words, they can only be made of the shape 1 x k (1 row, k columns) or k x 1 (k rows, 1 column), where k can be of any size. At least one horizontal or vertical cell separates between two battleships (i.e., there are no adjacent battleships). + + + +Example 1: + + +Input: board = [["X",".",".","X"],[".",".",".","X"],[".",".",".","X"]] +Output: 2 +Example 2: + +Input: board = [["."]] +Output: 0 + + +Constraints: + +m == board.length +n == board[i].length +1 <= m, n <= 200 +board[i][j] is either '.' or 'X'. + + +Follow up: Could you do it in one-pass, using only O(1) extra memory and without modifying the values board? +""" + + +class Solution: + def countBattleships(self, board: List[List[str]]) -> int: + """ + dfs + counting + since no adjacent + """ + self.dirs = [(0, -1), (0, 1), (1, 0), (-1, 0)] + self.M = len(board) + self.N = len(board[0]) + self.board = board + + visited = [ + [False for _ in range(self.N)] + for _ in range(self.M) + ] + ret = 0 + for i in range(self.M): + for j in range(self.N): + if board[i][j] == "X" and not visited[i][j]: + ret += 1 + self.dfs(i, j, visited) + + return ret + + def dfs(self, i, j, visited): + visited[i][j] = True + for di, dj in self.dirs: + I = i + di + J = j + dj + if 0 <= I < self.M and 0 <= J < self.N \ + and self.board[I][J] == "X" and not visited[I][J]: + self.dfs(I, J, visited) + \ No newline at end of file diff --git a/529 Minesweeper.py b/529 Minesweeper.py new file mode 100644 index 0000000..2eb2788 --- /dev/null +++ b/529 Minesweeper.py @@ -0,0 +1,3 @@ +""" + +""" \ No newline at end of file diff --git a/542 01 Matrix.py b/542 01 Matrix.py new file mode 100644 index 0000000..e26f15b --- /dev/null +++ b/542 01 Matrix.py @@ -0,0 +1,113 @@ +""" +Given an m x n binary matrix mat, return the distance of the nearest 0 for each cell. + +The distance between two cells sharing a common edge is 1. + + + +Example 1: + + +Input: mat = [[0,0,0],[0,1,0],[0,0,0]] +Output: [[0,0,0],[0,1,0],[0,0,0]] +Example 2: + + +Input: mat = [[0,0,0],[0,1,0],[1,1,1]] +Output: [[0,0,0],[0,1,0],[1,2,1]] + + +Constraints: + +m == mat.length +n == mat[i].length +1 <= m, n <= 104 +1 <= m * n <= 104 +mat[i][j] is either 0 or 1. +There is at least one 0 in mat. + +Note: This question is the same as 1765: https://leetcode.com/problems/map-of-highest-peak/ +""" +import sys +from collections import deque + + +class SolutionError: + def updateMatrix(self, mat: List[List[int]]) -> List[List[int]]: + """ + * dfs every cell + * with memoization + + dfs + visited + + dfs is wrong + """ + self.mat = mat + self.M = len(mat) + self.N = len(mat[0]) + self.dirs = [(0, -1), (0, 1), (1, 0), (-1, 0)] + visited = [ + [False for _ in range(self.N)] + for _ in range(self.M) + ] + ret = [ + [sys.maxsize for _ in range(self.N)] + for _ in range(self.M) + ] + for i in range(self.M): + for j in range(self.N): + self.nearest(i, j, visited, ret) + + return ret + + def nearest(self, i, j, visited, ret): + if visited[i][j]: + return ret[i][j] + + visited[i][j] = True + if self.mat[i][j] == 0: + ret[i][j] = 0 + return 0 + + for di, dj in self.dirs: + I = i + di + J = j + dj + if 0 <= I < self.M and 0 <= J < self.N: + nbr = self.nearest(I, J, visited, ret) + ret[i][j] = min(ret[i][j], nbr + 1) + + return ret[i][j] + + +class Solution: + def updateMatrix(self, mat: List[List[int]]) -> List[List[int]]: + """ + BFS + """ + q = deque() + M = len(mat) + N = len(mat[0]) + dirs = [(0, 1), (0, -1), (1, 0), (-1, 0)] + ret = [ + [sys.maxsize for _ in range(N)] + for _ in range(M) + ] + for i in range(M): + for j in range(N): + if mat[i][j] == 0: + ret[i][j] = 0 + q.append((i, j)) + + while q: + i, j = q.popleft() + for di, dj in dirs: + I = i + di + J = j + dj + # update nbr + if 0 <= I < M and 0 <= J < N: + cur = ret[i][j] + 1 + if ret[I][J] > cur: + ret[I][J] = cur + q.append((I, J)) # add to pending queue + + return ret From 1f042de7afb9f510c000d6faec4844734cee05ed Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 2 Apr 2025 19:32:42 -0400 Subject: [PATCH 343/344] update --- 1145 Binary Tree Coloring Game.py | 111 ++++++++++++++ ...Maximum Product of Splitted Binary Tree.py | 75 ++++++++++ 1361 Validate Binary Tree Nodes.py | 82 +++++++++++ 1367 Linked List in Binary Tree.py | 56 ++++++++ ...des in the Sub-Tree With the Same Label.py | 75 ++++++++++ 1600 Throne Inheritance.py | 135 ++++++++++++++++++ 1993 Operations on Tree.py | 126 ++++++++++++++++ 2049 Count Nodes With the Highest Score.py | 81 +++++++++++ ...ions From a Binary Tree Node to Another.py | 113 +++++++++++++++ 2467 Most Profitable Path in a Tree.py | 120 ++++++++++++++++ ...t Nodes Queries in a Binary Search Tree.py | 112 +++++++++++++++ 2583 Kth Largest Sum in a Binary Tree.py | 63 ++++++++ 2641 Cousins in Binary Tree II.py | 77 ++++++++++ 13 files changed, 1226 insertions(+) create mode 100644 1145 Binary Tree Coloring Game.py create mode 100644 1339 Maximum Product of Splitted Binary Tree.py create mode 100644 1361 Validate Binary Tree Nodes.py create mode 100644 1367 Linked List in Binary Tree.py create mode 100644 1519 Number of Nodes in the Sub-Tree With the Same Label.py create mode 100644 1600 Throne Inheritance.py create mode 100644 1993 Operations on Tree.py create mode 100644 2049 Count Nodes With the Highest Score.py create mode 100644 2096 Step-By-Step Directions From a Binary Tree Node to Another.py create mode 100644 2467 Most Profitable Path in a Tree.py create mode 100644 2476 Closest Nodes Queries in a Binary Search Tree.py create mode 100644 2583 Kth Largest Sum in a Binary Tree.py create mode 100644 2641 Cousins in Binary Tree II.py diff --git a/1145 Binary Tree Coloring Game.py b/1145 Binary Tree Coloring Game.py new file mode 100644 index 0000000..ddd1f7e --- /dev/null +++ b/1145 Binary Tree Coloring Game.py @@ -0,0 +1,111 @@ +""" +Two players play a turn based game on a binary tree. We are given the root of this binary tree, and the number of nodes n in the tree. n is odd, and each node has a distinct value from 1 to n. + +Initially, the first player names a value x with 1 <= x <= n, and the second player names a value y with 1 <= y <= n and y != x. The first player colors the node with value x red, and the second player colors the node with value y blue. + +Then, the players take turns starting with the first player. In each turn, that player chooses a node of their color (red if player 1, blue if player 2) and colors an uncolored neighbor of the chosen node (either the left child, right child, or parent of the chosen node.) + +If (and only if) a player cannot choose such a node in this way, they must pass their turn. If both players pass their turn, the game ends, and the winner is the player that colored more nodes. + +You are the second player. If it is possible to choose such a y to ensure you win the game, return true. If it is not possible, return false. + + + +Example 1: + + +Input: root = [1,2,3,4,5,6,7,8,9,10,11], n = 11, x = 3 +Output: true +Explanation: The second player can choose the node with value 2. +Example 2: + +Input: root = [1,2,3], n = 3, x = 1 +Output: false + + +Constraints: + +The number of nodes in the tree is n. +1 <= x <= n <= 100 +n is odd. +1 <= Node.val <= n +All the values of the tree are unique. +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class SolutionTwoPasses: + def btreeGameWinningMove(self, root: Optional[TreeNode], n: int, x: int) -> bool: + """ + taking control of the root with larger subtree + block the parent, left or right + """ + self.root_count = self.count(root) + self.ret = False + self.dfs(root, x) + return self.ret + + def count(self, cur): + if not cur: + return 0 + + s = 1 + s += self.count(cur.left) + s += self.count(cur.right) + return s + + def dfs(self, cur, x): + if not cur: + return 0 + + l = self.dfs(cur.left, x) + r = self.dfs(cur.right, x) + c = l + r + 1 + if cur.val == x: + # block left: + if l > self.root_count - l: + self.ret = True + # block right + if r > self.root_count - r: + self.ret = True + # block parent: + if self.root_count - c > c: + self.ret = True + + return c + + +class Solution: + def btreeGameWinningMove(self, root: Optional[TreeNode], n: int, x: int) -> bool: + self.x_left = 0 + self.x_right = 0 + self.x_root = 0 + root_count = self.dfs(root, x) + + if self.x_left > root_count - self.x_left: + return True + if self.x_right > root_count - self.x_right: + return True + if root_count - self.x_root > self.x_root: + return True + return False + + + def dfs(self, cur, x): + if not cur: + return 0 + + l = self.dfs(cur.left, x) + r = self.dfs(cur.right, x) + c = 1 + l + r + if cur.val == x: + self.x_left = l + self.x_right = r + self.x_root = c + + return c \ No newline at end of file diff --git a/1339 Maximum Product of Splitted Binary Tree.py b/1339 Maximum Product of Splitted Binary Tree.py new file mode 100644 index 0000000..c4877f3 --- /dev/null +++ b/1339 Maximum Product of Splitted Binary Tree.py @@ -0,0 +1,75 @@ +""" +Given the root of a binary tree, split the binary tree into two subtrees by removing one edge such that the product of the sums of the subtrees is maximized. + +Return the maximum product of the sums of the two subtrees. Since the answer may be too large, return it modulo 109 + 7. + +Note that you need to maximize the answer before taking the mod and not after taking it. + + + +Example 1: + + +Input: root = [1,2,3,4,5,6] +Output: 110 +Explanation: Remove the red edge and get 2 binary trees with sum 11 and 10. Their product is 110 (11*10) +Example 2: + + +Input: root = [1,null,2,3,4,null,null,5,6] +Output: 90 +Explanation: Remove the red edge and get 2 binary trees with sum 15 and 6.Their product is 90 (15*6) + + +Constraints: + +The number of nodes in the tree is in the range [2, 5 * 10^4]. +1 <= Node.val <= 10^4 +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +MOD = int(1e9+7) +class Solution: + def maxProduct(self, root: Optional[TreeNode]) -> int: + """ + Brute force: remove edge O(N) * calculate sum, O(N) + + cache sum O(1) + only need to root sum, and subtree sum] + + probably only need to store root sum, not every node's sum + """ + self.s = {} + self.dfs(root) + self.maxa = 0 + self.dfs_remove(root, root) + return self.maxa % MOD + + def dfs(self, cur): + if not cur: + return 0 + + s = cur.val + s += self.dfs(cur.left) + s += self.dfs(cur.right) + self.s[cur] = s + return s + + def dfs_remove(self, cur, root): + if not cur: + return + + for child in [cur.left, cur.right]: + if child: + a = self.s[child] + b = (self.s[root] - a) + self.maxa = max(self.maxa, a * b) + + self.dfs_remove(cur.left, root) + self.dfs_remove(cur.right, root) \ No newline at end of file diff --git a/1361 Validate Binary Tree Nodes.py b/1361 Validate Binary Tree Nodes.py new file mode 100644 index 0000000..8b4b655 --- /dev/null +++ b/1361 Validate Binary Tree Nodes.py @@ -0,0 +1,82 @@ +""" +You have n binary tree nodes numbered from 0 to n - 1 where node i has two children leftChild[i] and rightChild[i], return true if and only if all the given nodes form exactly one valid binary tree. + +If node i has no left child then leftChild[i] will equal -1, similarly for the right child. + +Note that the nodes have no values and that we only use the node numbers in this problem. + + + +Example 1: + + +Input: n = 4, leftChild = [1,-1,3,-1], rightChild = [2,-1,-1,-1] +Output: true +Example 2: + + +Input: n = 4, leftChild = [1,-1,3,-1], rightChild = [2,3,-1,-1] +Output: false +Example 3: + + +Input: n = 2, leftChild = [1,0], rightChild = [-1,-1] +Output: false + + +Constraints: + +n == leftChild.length == rightChild.length +1 <= n <= 10^4 +-1 <= leftChild[i], rightChild[i] <= n - 1 +""" +from collections import defaultdict + + +class Solution: + def validateBinaryTreeNodes(self, n: int, leftChild: List[int], rightChild: List[int]) -> bool: + """ + graph visit to check repeatetion, disconnetion, single root + """ + has_parent = [False for _ in range(n)] + visited = [False for _ in range(n)] + G = defaultdict(list) + for i in range(n): + if leftChild[i] >= 0: + has_parent[leftChild[i]] = True + G[i].append(leftChild[i]) + if rightChild[i] >= 0: + has_parent[rightChild[i]] = True + G[i].append(rightChild[i]) + + root = None + for i in range(n): + if not has_parent[i]: + if root is None: + root = i + else: + return False + + if root is None: + return False + + self.ret = True + self.dfs(G, root, visited) + if not self.ret: + return False + + for i in range(n): + if not visited[i]: + return False + + return True + + + def dfs(self, G, cur, visited): + if visited[cur]: + self.ret = False + return + + visited[cur] = True + for nbr in G[cur]: + self.dfs(G, nbr, visited) \ No newline at end of file diff --git a/1367 Linked List in Binary Tree.py b/1367 Linked List in Binary Tree.py new file mode 100644 index 0000000..d054b20 --- /dev/null +++ b/1367 Linked List in Binary Tree.py @@ -0,0 +1,56 @@ +""" +Given a binary tree root and a linked list with head as the first node. + +Return True if all the elements in the linked list starting from the head correspond to some downward path connected in the binary tree otherwise return False. + +In this context downward path means a path that starts at some node and goes downwards. + + + +Example 1: + + + +Input: head = [4,2,8], root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3] +Output: true +Explanation: Nodes in blue form a subpath in the binary Tree. +Example 2: + + + +Input: head = [1,4,2,6], root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3] +Output: true +Example 3: + +Input: head = [1,4,2,6,8], root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3] +Output: false +Explanation: There is no path in the binary tree that contains all the elements of the linked list from head. + + +Constraints: + +The number of nodes in the tree will be in the range [1, 2500]. +The number of nodes in the list will be in the range [1, 100]. +1 <= Node.val <= 100 for each node in the linked list and binary tree. +""" +class Solution: + def isSubPath(self, head: Optional[ListNode], root: Optional[TreeNode]) -> bool: + self.ret = False + self.dfs(head, root, False) + return self.ret + + def dfs(self, head, cur, found): + if head is None: + self.ret = True + return + + if cur is None: + return + + if cur.val == head.val: + self.dfs(head.next, cur.left, True) + self.dfs(head.next, cur.right, True) + + if not found: + self.dfs(head, cur.left, False) + self.dfs(head, cur.right, False) \ No newline at end of file diff --git a/1519 Number of Nodes in the Sub-Tree With the Same Label.py b/1519 Number of Nodes in the Sub-Tree With the Same Label.py new file mode 100644 index 0000000..5a5ce04 --- /dev/null +++ b/1519 Number of Nodes in the Sub-Tree With the Same Label.py @@ -0,0 +1,75 @@ +""" +You are given a tree (i.e. a connected, undirected graph that has no cycles) consisting of n nodes numbered from 0 to n - 1 and exactly n - 1 edges. The root of the tree is the node 0, and each node of the tree has a label which is a lower-case character given in the string labels (i.e. The node with the number i has the label labels[i]). + +The edges array is given on the form edges[i] = [ai, bi], which means there is an edge between nodes ai and bi in the tree. + +Return an array of size n where ans[i] is the number of nodes in the subtree of the ith node which have the same label as node i. + +A subtree of a tree T is the tree consisting of a node in T and all of its descendant nodes. + + + +Example 1: + + +Input: n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], labels = "abaedcd" +Output: [2,1,1,1,1,1,1] +Explanation: Node 0 has label 'a' and its sub-tree has node 2 with label 'a' as well, thus the answer is 2. Notice that any node is part of its sub-tree. +Node 1 has a label 'b'. The sub-tree of node 1 contains nodes 1,4 and 5, as nodes 4 and 5 have different labels than node 1, the answer is just 1 (the node itself). +Example 2: + + +Input: n = 4, edges = [[0,1],[1,2],[0,3]], labels = "bbbb" +Output: [4,2,1,1] +Explanation: The sub-tree of node 2 contains only node 2, so the answer is 1. +The sub-tree of node 3 contains only node 3, so the answer is 1. +The sub-tree of node 1 contains nodes 1 and 2, both have label 'b', thus the answer is 2. +The sub-tree of node 0 contains nodes 0, 1, 2 and 3, all with label 'b', thus the answer is 4. +Example 3: + + +Input: n = 5, edges = [[0,1],[0,2],[1,3],[0,4]], labels = "aabab" +Output: [3,2,1,1,1] + + +Constraints: + +1 <= n <= 10^5 +edges.length == n - 1 +edges[i].length == 2 +0 <= ai, bi < n +ai != bi +labels.length == n +labels is consisting of only of lowercase English letters. +""" +from collections import Counter, defaultdict + + +class Solution: + def countSubTrees(self, n: int, edges: List[List[int]], labels: str) -> List[int]: + """ + just collect all labels in hm + """ + self.labels = labels + self.ret = [0 for _ in range(n)] + G = defaultdict(list) + for u, v in edges: + G[u].append(v) + G[v].append(u) + + self.dfs(G, 0, defaultdict(bool)) + return self.ret + + def dfs(self, G, cur, visited): + if visited[cur]: + return Counter() + + visited[cur] = True + cnt = Counter() + cnt[self.labels[cur]] = 1 + for nbr in G[cur]: + if not visited[nbr]: + cnt += self.dfs(G, nbr, visited) + + self.ret[cur] = cnt[self.labels[cur]] + return cnt diff --git a/1600 Throne Inheritance.py b/1600 Throne Inheritance.py new file mode 100644 index 0000000..3ee9abd --- /dev/null +++ b/1600 Throne Inheritance.py @@ -0,0 +1,135 @@ +""" +A kingdom consists of a king, his children, his grandchildren, and so on. Every once in a while, someone in the family dies or a child is born. + +The kingdom has a well-defined order of inheritance that consists of the king as the first member. Let's define the recursive function Successor(x, curOrder), which given a person x and the inheritance order so far, returns who should be the next person after x in the order of inheritance. + +Successor(x, curOrder): + if x has no children or all of x's children are in curOrder: + if x is the king return null + else return Successor(x's parent, curOrder) + else return x's oldest child who's not in curOrder +For example, assume we have a kingdom that consists of the king, his children Alice and Bob (Alice is older than Bob), and finally Alice's son Jack. + +In the beginning, curOrder will be ["king"]. +Calling Successor(king, curOrder) will return Alice, so we append to curOrder to get ["king", "Alice"]. +Calling Successor(Alice, curOrder) will return Jack, so we append to curOrder to get ["king", "Alice", "Jack"]. +Calling Successor(Jack, curOrder) will return Bob, so we append to curOrder to get ["king", "Alice", "Jack", "Bob"]. +Calling Successor(Bob, curOrder) will return null. Thus the order of inheritance will be ["king", "Alice", "Jack", "Bob"]. +Using the above function, we can always obtain a unique order of inheritance. + +Implement the ThroneInheritance class: + +ThroneInheritance(string kingName) Initializes an object of the ThroneInheritance class. The name of the king is given as part of the constructor. +void birth(string parentName, string childName) Indicates that parentName gave birth to childName. +void death(string name) Indicates the death of name. The death of the person doesn't affect the Successor function nor the current inheritance order. You can treat it as just marking the person as dead. +string[] getInheritanceOrder() Returns a list representing the current order of inheritance excluding dead people. + + +Example 1: + +Input +["ThroneInheritance", "birth", "birth", "birth", "birth", "birth", "birth", "getInheritanceOrder", "death", "getInheritanceOrder"] +[["king"], ["king", "andy"], ["king", "bob"], ["king", "catherine"], ["andy", "matthew"], ["bob", "alex"], ["bob", "asha"], [null], ["bob"], [null]] +Output +[null, null, null, null, null, null, null, ["king", "andy", "matthew", "bob", "alex", "asha", "catherine"], null, ["king", "andy", "matthew", "alex", "asha", "catherine"]] + +Explanation +ThroneInheritance t= new ThroneInheritance("king"); // order: king +t.birth("king", "andy"); // order: king > andy +t.birth("king", "bob"); // order: king > andy > bob +t.birth("king", "catherine"); // order: king > andy > bob > catherine +t.birth("andy", "matthew"); // order: king > andy > matthew > bob > catherine +t.birth("bob", "alex"); // order: king > andy > matthew > bob > alex > catherine +t.birth("bob", "asha"); // order: king > andy > matthew > bob > alex > asha > catherine +t.getInheritanceOrder(); // return ["king", "andy", "matthew", "bob", "alex", "asha", "catherine"] +t.death("bob"); // order: king > andy > matthew > bob > alex > asha > catherine +t.getInheritanceOrder(); // return ["king", "andy", "matthew", "alex", "asha", "catherine"] + + +Constraints: + +1 <= kingName.length, parentName.length, childName.length, name.length <= 15 +kingName, parentName, childName, and name consist of lowercase English letters only. +All arguments childName and kingName are distinct. +All name arguments of death will be passed to either the constructor or as childName to birth first. +For each call to birth(parentName, childName), it is guaranteed that parentName is alive. +At most 10^5 calls will be made to birth and death. +At most 10 calls will be made to getInheritanceOrder. +""" +from collections import defaultdict + + +class ThroneInheritanceBruteForce: + + def __init__(self, kingName: str): + self.root = kingName + self.deleted = set() + self.G = defaultdict(list) + self.P = defaultdict(str) + + def birth(self, parentName: str, childName: str) -> None: + self.G[parentName].append(childName) + self.P[childName] = parentName + + def death(self, name: str) -> None: + self.deleted.add(name) + + def getInheritanceOrder(self) -> List[str]: + order = [] + self.succeed(self.root, order, defaultdict(bool)) + + ret = [] + for e in order: + if e not in self.deleted: + ret.append(e) + + return ret + + def succeed(self, cur, order, visited): + if not visited[cur]: + order.append(cur) + visited[cur] = True + + valid = False + for c in self.G[cur]: + if not visited[c]: + valid = True + self.succeed(c, order, visited) + + if not valid: + if cur == self.root: + return + self.succeed(self.P[cur], order, visited) + + +class ThroneInheritance: + def __init__(self, kingName: str): + self.root = kingName + self.deleted = set() + self.G = defaultdict(list) + + def birth(self, parentName: str, childName: str) -> None: + self.G[parentName].append(childName) + + def death(self, name: str) -> None: + self.deleted.add(name) + + def getInheritanceOrder(self) -> List[str]: + order = [] + self.succeed(self.root, order, defaultdict(bool)) + return order + + def succeed(self, cur, order, visited): + if cur not in self.deleted: + order.append(cur) + + visited[cur] = True + for c in self.G[cur]: + if not visited[c]: + self.succeed(c, order, visited) + +# Your ThroneInheritance object will be instantiated and called as such: +# obj = ThroneInheritance(kingName) +# obj.birth(parentName,childName) +# obj.death(name) +# param_3 = obj.getInheritanceOrder() \ No newline at end of file diff --git a/1993 Operations on Tree.py b/1993 Operations on Tree.py new file mode 100644 index 0000000..eb1240e --- /dev/null +++ b/1993 Operations on Tree.py @@ -0,0 +1,126 @@ +""" +You are given a tree with n nodes numbered from 0 to n - 1 in the form of a parent array parent where parent[i] is the parent of the ith node. The root of the tree is node 0, so parent[0] = -1 since it has no parent. You want to design a data structure that allows users to lock, unlock, and upgrade nodes in the tree. + +The data structure should support the following functions: + +Lock: Locks the given node for the given user and prevents other users from locking the same node. You may only lock a node using this function if the node is unlocked. +Unlock: Unlocks the given node for the given user. You may only unlock a node using this function if it is currently locked by the same user. +Upgrade: Locks the given node for the given user and unlocks all of its descendants regardless of who locked it. You may only upgrade a node if all 3 conditions are true: +The node is unlocked, +It has at least one locked descendant (by any user), and +It does not have any locked ancestors. +Implement the LockingTree class: + +LockingTree(int[] parent) initializes the data structure with the parent array. +lock(int num, int user) returns true if it is possible for the user with id user to lock the node num, or false otherwise. If it is possible, the node num will become locked by the user with id user. +unlock(int num, int user) returns true if it is possible for the user with id user to unlock the node num, or false otherwise. If it is possible, the node num will become unlocked. +upgrade(int num, int user) returns true if it is possible for the user with id user to upgrade the node num, or false otherwise. If it is possible, the node num will be upgraded. + + +Example 1: + + +Input +["LockingTree", "lock", "unlock", "unlock", "lock", "upgrade", "lock"] +[[[-1, 0, 0, 1, 1, 2, 2]], [2, 2], [2, 3], [2, 2], [4, 5], [0, 1], [0, 1]] +Output +[null, true, false, true, true, true, false] + +Explanation +LockingTree lockingTree = new LockingTree([-1, 0, 0, 1, 1, 2, 2]); +lockingTree.lock(2, 2); // return true because node 2 is unlocked. + // Node 2 will now be locked by user 2. +lockingTree.unlock(2, 3); // return false because user 3 cannot unlock a node locked by user 2. +lockingTree.unlock(2, 2); // return true because node 2 was previously locked by user 2. + // Node 2 will now be unlocked. +lockingTree.lock(4, 5); // return true because node 4 is unlocked. + // Node 4 will now be locked by user 5. +lockingTree.upgrade(0, 1); // return true because node 0 is unlocked and has at least one locked descendant (node 4). + // Node 0 will now be locked by user 1 and node 4 will now be unlocked. +lockingTree.lock(0, 1); // return false because node 0 is already locked. + + +Constraints: + +n == parent.length +2 <= n <= 2000 +0 <= parent[i] <= n - 1 for i != 0 +parent[0] == -1 +0 <= num <= n - 1 +1 <= user <= 10^4 +parent represents a valid tree. +At most 2000 calls in total will be made to lock, unlock, and upgrade. +""" +from collections import defaultdict + + +class LockingTree: + def __init__(self, parent: List[int]): + self.G = defaultdict(list) + self.P = defaultdict(int) + for i, p in enumerate(parent): + if i != 0: + self.G[p].append(i) + self.P[i] = p + + self.locks = defaultdict(int) + + def lock(self, num: int, user: int) -> bool: + if num in self.locks: + return False + + self.locks[num] = user + return True + + def unlock(self, num: int, user: int) -> bool: + if num in self.locks and self.locks[num] == user: + del self.locks[num] + return True + + return False + + def upgrade(self, num: int, user: int) -> bool: + if num in self.locks: + return False + + cur = num + while cur in self.P: + if self.P[cur] in self.locks: + return False + cur = self.P[cur] + + for c in self.G[num]: + if self.dfs(c): + break # break for-else statement + else: + return False + + self.locks[num] = user + for c in self.G[num]: + self.dfs_unlock(c) + + return True + + def dfs(self, cur): + if cur in self.locks: + return True + + for c in self.G[cur]: + if self.dfs(c): + return True + + return False + + def dfs_unlock(self, cur): + if cur in self.locks: + del self.locks[cur] + + for c in self.G[cur]: + self.dfs_unlock(c) + + +# Your LockingTree object will be instantiated and called as such: +# obj = LockingTree(parent) +# param_1 = obj.lock(num,user) +# param_2 = obj.unlock(num,user) +# param_3 = obj.upgrade(num,user) \ No newline at end of file diff --git a/2049 Count Nodes With the Highest Score.py b/2049 Count Nodes With the Highest Score.py new file mode 100644 index 0000000..861e21d --- /dev/null +++ b/2049 Count Nodes With the Highest Score.py @@ -0,0 +1,81 @@ +""" +There is a binary tree rooted at 0 consisting of n nodes. The nodes are labeled from 0 to n - 1. You are given a 0-indexed integer array parents representing the tree, where parents[i] is the parent of node i. Since node 0 is the root, parents[0] == -1. + +Each node has a score. To find the score of a node, consider if the node and the edges connected to it were removed. The tree would become one or more non-empty subtrees. The size of a subtree is the number of the nodes in it. The score of the node is the product of the sizes of all those subtrees. + +Return the number of nodes that have the highest score. + + + +Example 1: + +example-1 +Input: parents = [-1,2,0,2,0] +Output: 3 +Explanation: +- The score of node 0 is: 3 * 1 = 3 +- The score of node 1 is: 4 = 4 +- The score of node 2 is: 1 * 1 * 2 = 2 +- The score of node 3 is: 4 = 4 +- The score of node 4 is: 4 = 4 +The highest score is 4, and three nodes (node 1, node 3, and node 4) have the highest score. +Example 2: + +example-2 +Input: parents = [-1,2,0] +Output: 2 +Explanation: +- The score of node 0 is: 2 = 2 +- The score of node 1 is: 2 = 2 +- The score of node 2 is: 1 * 1 = 1 +The highest score is 2, and two nodes (node 0 and node 1) have the highest score. + + +Constraints: + +n == parents.length +2 <= n <= 10^5 +parents[0] == -1 +0 <= parents[i] <= n - 1 for i != 0 +parents represents a valid binary tree. +""" +from collections import defaultdict + + +class Solution: + def countHighestScoreNodes(self, parents: List[int]) -> int: + """ + 3 parts: + * l + * r + * total - (l+r+1) + """ + self.N = len(parents) + G = defaultdict(list) + for i, v in enumerate(parents): + G[v].append(i) + + self.maxa = 0 + self.max_count = 0 + self.count(G, 0) + return self.max_count + + def count(self, G, cur): + A = [] + for nbr in G[cur]: + A.append(self.count(G, nbr)) + + c = sum(A) + 1 + + product = 1 + for a in A: + product *= max(1, a) + product *= max(1, self.N - c) + + if product > self.maxa: + self.maxa = product + self.max_count = 1 + elif product == self.maxa: + self.max_count += 1 + + return c diff --git a/2096 Step-By-Step Directions From a Binary Tree Node to Another.py b/2096 Step-By-Step Directions From a Binary Tree Node to Another.py new file mode 100644 index 0000000..19e52c6 --- /dev/null +++ b/2096 Step-By-Step Directions From a Binary Tree Node to Another.py @@ -0,0 +1,113 @@ +""" +You are given the root of a binary tree with n nodes. Each node is uniquely assigned a value from 1 to n. You are also given an integer startValue representing the value of the start node s, and a different integer destValue representing the value of the destination node t. + +Find the shortest path starting from node s and ending at node t. Generate step-by-step directions of such path as a string consisting of only the uppercase letters 'L', 'R', and 'U'. Each letter indicates a specific direction: + +'L' means to go from a node to its left child node. +'R' means to go from a node to its right child node. +'U' means to go from a node to its parent node. +Return the step-by-step directions of the shortest path from node s to node t. + + + +Example 1: + + +Input: root = [5,1,2,3,null,6,4], startValue = 3, destValue = 6 +Output: "UURL" +Explanation: The shortest path is: 3 → 1 → 5 → 2 → 6. +Example 2: + + +Input: root = [2,1], startValue = 2, destValue = 1 +Output: "L" +Explanation: The shortest path is: 2 → 1. + + +Constraints: + +The number of nodes in the tree is n. +2 <= n <= 10^5 +1 <= Node.val <= n +All the values in the tree are unique. +1 <= startValue, destValue <= n +startValue != destValue +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def getDirections(self, root: Optional[TreeNode], startValue: int, destValue: int) -> str: + """ + find the LCA first, then reconstruct the path + + do it one path + dfs -> has_start, has_dest, path + path use LR, it can degrade to U + path to itself + """ + self.start = startValue + self.dest = destValue + self.ret = None + self.dfs(root) + return self.ret + + + def dfs(self, cur): + if not cur: + return (False, False, []) + + s = False + d = False + p = [] + if cur.val == self.start: + s = True + if cur.val == self.dest: + d = True + + ls, ld, lp = self.dfs(cur.left) + rs, rd, rp = self.dfs(cur.right) + if (ls ^ rs ^ s) & (ld ^ rd ^ d) & self.ret is None: + if ls: + builder = ["U" for _ in lp] + builder.append("U") + if rd: + builder.append("R") + builder += rp[::-1] + self.ret = "".join(builder) + elif rs: + builder = ["U" for _ in rp] + builder.append("U") + if ld: + builder.append("L") + builder += lp[::-1] + self.ret = "".join(builder) + elif s: + builder = [] + if ld: + builder.append("L") + builder += lp[::-1] + else: + builder.append("R") + builder += rp[::-1] + self.ret = "".join(builder) + + if s | d: + p = [] + elif ls | ld: + p = lp + p.append("L") + elif rs | rd: + p = rp + p.append("R") + + return ( + ls | rs | s, + ld | rd | d, + p + ) \ No newline at end of file diff --git a/2467 Most Profitable Path in a Tree.py b/2467 Most Profitable Path in a Tree.py new file mode 100644 index 0000000..74b9d10 --- /dev/null +++ b/2467 Most Profitable Path in a Tree.py @@ -0,0 +1,120 @@ +""" +There is an undirected tree with n nodes labeled from 0 to n - 1, rooted at node 0. You are given a 2D integer array edges of length n - 1 where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the tree. + +At every node i, there is a gate. You are also given an array of even integers amount, where amount[i] represents: + +the price needed to open the gate at node i, if amount[i] is negative, or, +the cash reward obtained on opening the gate at node i, otherwise. +The game goes on as follows: + +Initially, Alice is at node 0 and Bob is at node bob. +At every second, Alice and Bob each move to an adjacent node. Alice moves towards some leaf node, while Bob moves towards node 0. +For every node along their path, Alice and Bob either spend money to open the gate at that node, or accept the reward. Note that: +If the gate is already open, no price will be required, nor will there be any cash reward. +If Alice and Bob reach the node simultaneously, they share the price/reward for opening the gate there. In other words, if the price to open the gate is c, then both Alice and Bob pay c / 2 each. Similarly, if the reward at the gate is c, both of them receive c / 2 each. +If Alice reaches a leaf node, she stops moving. Similarly, if Bob reaches node 0, he stops moving. Note that these events are independent of each other. +Return the maximum net income Alice can have if she travels towards the optimal leaf node. + + + +Example 1: + + +Input: edges = [[0,1],[1,2],[1,3],[3,4]], bob = 3, amount = [-2,4,2,-4,6] +Output: 6 +Explanation: +The above diagram represents the given tree. The game goes as follows: +- Alice is initially on node 0, Bob on node 3. They open the gates of their respective nodes. + Alice's net income is now -2. +- Both Alice and Bob move to node 1. + Since they reach here simultaneously, they open the gate together and share the reward. + Alice's net income becomes -2 + (4 / 2) = 0. +- Alice moves on to node 3. Since Bob already opened its gate, Alice's income remains unchanged. + Bob moves on to node 0, and stops moving. +- Alice moves on to node 4 and opens the gate there. Her net income becomes 0 + 6 = 6. +Now, neither Alice nor Bob can make any further moves, and the game ends. +It is not possible for Alice to get a higher net income. +Example 2: + + +Input: edges = [[0,1]], bob = 1, amount = [-7280,2350] +Output: -7280 +Explanation: +Alice follows the path 0->1 whereas Bob follows the path 1->0. +Thus, Alice opens the gate at node 0 only. Hence, her net income is -7280. + + +Constraints: + +2 <= n <= 10^5 +edges.length == n - 1 +edges[i].length == 2 +0 <= ai, bi < n +ai != bi +edges represents a valid tree. +1 <= bob < n +amount.length == n +amount[i] is an even integer in the range [-10^4, 10^4]. +""" +from collections import defaultdict +import sys + + +class Solution: + def mostProfitablePath(self, edges: List[List[int]], bob: int, amount: List[int]) -> int: + """ + Some dp involved? + Alice DFS all leaves + Bob only have one path, towards 0. BFS + BFS, remeber the pi node + DFS is also okay + count step -> then now we know the reward of alice + """ + G = defaultdict(list) + for a, b in edges: + G[a].append(b) + G[b].append(a) + + self.depths = defaultdict(int) + self.pi = defaultdict(int) + self.dfs_bob(G, bob, -1, 0, defaultdict(bool)) + # bob's path + self.path = set() + cur = 0 + while cur != -1: + self.path.add(cur) + cur = self.pi[cur] + + self.maxa = -sys.maxsize-1 + self.amounts = amount + self.dfs_alice(G, 0, 0, 0, defaultdict(bool)) + return self.maxa + + def dfs_alice(self, G, cur, reward, depth, visited): + visited[cur] = True + if cur not in self.path: + reward += self.amounts[cur] + else: + if depth < self.depths[cur]: + reward += self.amounts[cur] + elif depth == self.depths[cur]: + reward += self.amounts[cur] // 2 + else: + # already open + pass + + # leaf node + if len(G[cur]) == 1 and visited[G[cur][0]]: + self.maxa = max(self.maxa, reward) + + for nbr in G[cur]: + if not visited[nbr]: + self.dfs_alice(G, nbr, reward, depth+1, visited) + + def dfs_bob(self, G, cur, prev, depth, visited): + visited[cur] = True + self.depths[cur] = depth + self.pi[cur] = prev + for nbr in G[cur]: + if not visited[nbr]: + self.dfs_bob(G, nbr, cur, depth+1, visited) diff --git a/2476 Closest Nodes Queries in a Binary Search Tree.py b/2476 Closest Nodes Queries in a Binary Search Tree.py new file mode 100644 index 0000000..be13c41 --- /dev/null +++ b/2476 Closest Nodes Queries in a Binary Search Tree.py @@ -0,0 +1,112 @@ +""" +You are given the root of a binary search tree and an array queries of size n consisting of positive integers. + +Find a 2D array answer of size n where answer[i] = [mini, maxi]: + +mini is the largest value in the tree that is smaller than or equal to queries[i]. If a such value does not exist, add -1 instead. +maxi is the smallest value in the tree that is greater than or equal to queries[i]. If a such value does not exist, add -1 instead. +Return the array answer. + + + +Example 1: + + +Input: root = [6,2,13,1,4,9,15,null,null,null,null,null,null,14], queries = [2,5,16] +Output: [[2,2],[4,6],[15,-1]] +Explanation: We answer the queries in the following way: +- The largest number that is smaller or equal than 2 in the tree is 2, and the smallest number that is greater or equal than 2 is still 2. So the answer for the first query is [2,2]. +- The largest number that is smaller or equal than 5 in the tree is 4, and the smallest number that is greater or equal than 5 is 6. So the answer for the second query is [4,6]. +- The largest number that is smaller or equal than 16 in the tree is 15, and the smallest number that is greater or equal than 16 does not exist. So the answer for the third query is [15,-1]. +Example 2: + + +Input: root = [4,null,9], queries = [3] +Output: [[-1,4]] +Explanation: The largest number that is smaller or equal to 3 in the tree does not exist, and the smallest number that is greater or equal to 3 is 4. So the answer for the query is [-1,4]. + + +Constraints: + +The number of nodes in the tree is in the range [2, 10^5]. +1 <= Node.val <= 10^6 +n == queries.length +1 <= n <= 10^5 +1 <= queries[i] <= 10^6 +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class SolutionUnbalancedTLE: + def closestNodes(self, root: Optional[TreeNode], queries: List[int]) -> List[List[int]]: + ret = [] + for t in queries: + ret.append([self.find_lo(root, t), self.find_hi(root, t)]) + + return ret + + def find_lo(self, cur, target): + if not cur: + return -1 + + if target < cur.val: + return self.find_lo(cur.left, target) + elif target > cur.val: + ret = self.find_lo(cur.right, target) + if ret == -1: + return cur.val + return ret + else: + return cur.val + + def find_hi(self, cur, target): + if not cur: + return -1 + if target < cur.val: + ret = self.find_hi(cur.left, target) + if ret == -1: + return cur.val + return ret + elif target > cur.val: + return self.find_hi(cur.right, target) + else: + return cur.val + + +import bisect + + +class Solution: + def closestNodes(self, root: Optional[TreeNode], queries: List[int]) -> List[List[int]]: + A = [] + self.inorder(root, A) + ret = [] + for q in queries: + idx = bisect.bisect_left(A, q) + if idx < len(A) and A[idx] == q: + lo = A[idx] + else: + lo = A[idx-1] if idx > 0 else -1 + + idx = bisect.bisect_right(A, q) + if idx > 0 and A[idx-1] == q: + hi = A[idx-1] + else: + hi = A[idx] if idx < len(A) else -1 + + ret.append([lo, hi]) + + return ret + + def inorder(self, cur, A): + if not cur: + return + + self.inorder(cur.left, A) + A.append(cur.val) + self.inorder(cur.right, A) \ No newline at end of file diff --git a/2583 Kth Largest Sum in a Binary Tree.py b/2583 Kth Largest Sum in a Binary Tree.py new file mode 100644 index 0000000..0f3ac55 --- /dev/null +++ b/2583 Kth Largest Sum in a Binary Tree.py @@ -0,0 +1,63 @@ +""" +You are given the root of a binary tree and a positive integer k. + +The level sum in the tree is the sum of the values of the nodes that are on the same level. + +Return the kth largest level sum in the tree (not necessarily distinct). If there are fewer than k levels in the tree, return -1. + +Note that two nodes are on the same level if they have the same distance from the root. + + + +Example 1: + + +Input: root = [5,8,9,2,1,3,7,4,6], k = 2 +Output: 13 +Explanation: The level sums are the following: +- Level 1: 5. +- Level 2: 8 + 9 = 17. +- Level 3: 2 + 1 + 3 + 7 = 13. +- Level 4: 4 + 6 = 10. +The 2nd largest level sum is 13. +Example 2: + + +Input: root = [1,2,null,3], k = 1 +Output: 3 +Explanation: The largest level sum is 3. + +Constraints: + +The number of nodes in the tree is n. +2 <= n <= 10^5 +1 <= Node.val <= 10^6 +1 <= k <= n +""" +class Solution: + def kthLargestLevelSum(self, root: Optional[TreeNode], k: int) -> int: + """ + BFS + find kth O(lg n) + """ + A = [] + q = [root] + while q: + new_q = [] + cur = 0 + for n in q: + cur += n.val + if n.left: + new_q.append(n.left) + if n.right: + new_q.append(n.right) + + A.append(cur) + q = new_q + + A.sort(reverse=True) + if k > len(A): + return -1 + + return A[k-1] + diff --git a/2641 Cousins in Binary Tree II.py b/2641 Cousins in Binary Tree II.py new file mode 100644 index 0000000..8ae07f0 --- /dev/null +++ b/2641 Cousins in Binary Tree II.py @@ -0,0 +1,77 @@ +""" +Given the root of a binary tree, replace the value of each node in the tree with the sum of all its cousins' values. + +Two nodes of a binary tree are cousins if they have the same depth with different parents. + +Return the root of the modified tree. + +Note that the depth of a node is the number of edges in the path from the root node to it. + + + +Example 1: + + +Input: root = [5,4,9,1,10,null,7] +Output: [0,0,0,7,7,null,11] +Explanation: The diagram above shows the initial binary tree and the binary tree after changing the value of each node. +- Node with value 5 does not have any cousins so its sum is 0. +- Node with value 4 does not have any cousins so its sum is 0. +- Node with value 9 does not have any cousins so its sum is 0. +- Node with value 1 has a cousin with value 7 so its sum is 7. +- Node with value 10 has a cousin with value 7 so its sum is 7. +- Node with value 7 has cousins with values 1 and 10 so its sum is 11. +Example 2: + + +Input: root = [3,1,2] +Output: [0,0,0] +Explanation: The diagram above shows the initial binary tree and the binary tree after changing the value of each node. +- Node with value 3 does not have any cousins so its sum is 0. +- Node with value 1 does not have any cousins so its sum is 0. +- Node with value 2 does not have any cousins so its sum is 0. +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def replaceValueInTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + """ + brute force + BFS, keep track of pi + + BFS + sum q + then, through pi, we know left and right + """ + root.val = 0 + q = [root] + while q: + new_q = [] + for cur in q: + if cur.left: + new_q.append(cur.left) + if cur.right: + new_q.append(cur.right) + + s = sum(e.val for e in new_q) + for cur in q: + children_sum = 0 + if cur.left: + children_sum += cur.left.val + if cur.right: + children_sum += cur.right.val + + if cur.left: + cur.left.val = s - children_sum + if cur.right: + cur.right.val = s - children_sum + + q = new_q + + return root \ No newline at end of file From 67a72ae971b447f12dd65f1780fb86b9769e2e57 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 3 Apr 2025 21:38:37 -0400 Subject: [PATCH 344/344] update --- ...olumns For Maximum Number of Equal Rows.py | 72 +++++++++++ 1091 Shortest Path in Binary Matrix.py | 71 +++++++++++ 1139 Largest 1-Bordered Square.py | 68 +++++++++++ 1219 Path with Maximum Gold.py | 112 ++++++++++++++++++ 1765 Map of Highest Peak.py | 84 +++++++++++++ 835 Image Overlap.py | 67 +++++++++++ 840 Magic Squares In Grid.py | 90 ++++++++++++++ 7 files changed, 564 insertions(+) create mode 100644 1072 Flip Columns For Maximum Number of Equal Rows.py create mode 100644 1091 Shortest Path in Binary Matrix.py create mode 100644 1139 Largest 1-Bordered Square.py create mode 100644 1219 Path with Maximum Gold.py create mode 100644 1765 Map of Highest Peak.py create mode 100644 835 Image Overlap.py create mode 100644 840 Magic Squares In Grid.py diff --git a/1072 Flip Columns For Maximum Number of Equal Rows.py b/1072 Flip Columns For Maximum Number of Equal Rows.py new file mode 100644 index 0000000..620bf37 --- /dev/null +++ b/1072 Flip Columns For Maximum Number of Equal Rows.py @@ -0,0 +1,72 @@ +""" +You are given an m x n binary matrix matrix. + +You can choose any number of columns in the matrix and flip every cell in that column (i.e., Change the value of the cell from 0 to 1 or vice versa). + +Return the maximum number of rows that have all values equal after some number of flips. + + + +Example 1: + +Input: matrix = [[0,1],[1,1]] +Output: 1 +Explanation: After flipping no values, 1 row has all values equal. +Example 2: + +Input: matrix = [[0,1],[1,0]] +Output: 2 +Explanation: After flipping values in the first column, both rows have equal values. +Example 3: + +Input: matrix = [[0,0,0],[0,0,1],[1,1,0]] +Output: 2 +Explanation: After flipping values in the first two columns, the last two rows have equal values. + + +Constraints: + +m == matrix.length +n == matrix[i].length +1 <= m, n <= 300 +matrix[i][j] is either 0 or 1. +""" +from collections import defaultdict + + +class Solution: + def maxEqualRowsAfterFlips(self, mat: List[List[int]]) -> int: + """ + 01 + 11 + + 01 + 10 + + 000 + 001 + 110 + + brute force + O(2^N) * O(N^2) + + Each row's pattern is determined by grouping contiguous blocks of identical values. For instance: + Row [0, 0, 0, 1, 1, 0, 0] produces the pattern: ***|**|**| + Row [0, 1, 1, 1, 1, 1, 0] produces the pattern: *|*****|*| + + The solution is simply the frequency of the most common pattern across all rows in the matrix. + """ + M = len(mat) + N = len(mat[0]) + cnt = defaultdict(int) + mask = (1 << N) - 1 + for i in range(M): + cur = 0 + for j in range(N): + cur <<= 1 + cur += mat[i][j] + + cnt[cur] += 1 + cnt[(cur ^ mask) & mask] += 1 + + return max(cnt.values()) \ No newline at end of file diff --git a/1091 Shortest Path in Binary Matrix.py b/1091 Shortest Path in Binary Matrix.py new file mode 100644 index 0000000..67ad2c2 --- /dev/null +++ b/1091 Shortest Path in Binary Matrix.py @@ -0,0 +1,71 @@ +""" +Given an n x n binary matrix grid, return the length of the shortest clear path in the matrix. If there is no clear path, return -1. + +A clear path in a binary matrix is a path from the top-left cell (i.e., (0, 0)) to the bottom-right cell (i.e., (n - 1, n - 1)) such that: + +All the visited cells of the path are 0. +All the adjacent cells of the path are 8-directionally connected (i.e., they are different and they share an edge or a corner). +The length of a clear path is the number of visited cells of this path. + + + +Example 1: + + +Input: grid = [[0,1],[1,0]] +Output: 2 +Example 2: + + +Input: grid = [[0,0,0],[1,1,0],[1,1,0]] +Output: 4 +Example 3: + +Input: grid = [[1,0,0],[1,1,0],[1,1,0]] +Output: -1 + + +Constraints: + +n == grid.length +n == grid[i].length +1 <= n <= 100 +grid[i][j] is 0 or 1 +""" +import sys + + +class Solution: + def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int: + """ + BFS + """ + if grid[0][0] != 0: + return -1 + + M = len(grid) + N = len(grid[0]) + dirs = [(0, -1), (0, 1), (-1, 0), (1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)] + q = [(0, 0)] + dist = [ + [sys.maxsize for _ in range(N)] + for _ in range(M) + ] + dist[0][0] = 1 + while q: + new_q = [] + for i, j in q: + d = dist[i][j] + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < M and 0 <= J < N and grid[I][J] == 0: + if dist[I][J] > d + 1: + dist[I][J] = d + 1 + new_q.append((I, J)) + q = new_q + + ret = dist[M-1][N-1] + if ret != sys.maxsize: + return ret + return -1 \ No newline at end of file diff --git a/1139 Largest 1-Bordered Square.py b/1139 Largest 1-Bordered Square.py new file mode 100644 index 0000000..80973ae --- /dev/null +++ b/1139 Largest 1-Bordered Square.py @@ -0,0 +1,68 @@ +""" +Given a 2D grid of 0s and 1s, return the number of elements in the largest square subgrid that has all 1s on its border, or 0 if such a subgrid doesn't exist in the grid. + +Example 1: + +Input: grid = [[1,1,1],[1,0,1],[1,1,1]] +Output: 9 +Example 2: + +Input: grid = [[1,1,0,0]] +Output: 1 + + +Constraints: + +1 <= grid.length <= 100 +1 <= grid[0].length <= 100 +grid[i][j] is 0 or 1 +""" +class Solution: + def largest1BorderedSquare(self, grid: List[List[int]]) -> int: + """ + 111 + 101 + 111 + + brute force, check boarder + + Reduce from 2D to 1D? + + 111 + 011 + 111 + number of consecutive 1 ending at i, j + + then check 4 boarders + """ + M = len(grid) + N = len(grid[0]) + # row + R = [ + [0 for _ in range(N+1)] + for _ in range(M+1) + ] + # col + C = [ + [0 for _ in range(N+1)] + for _ in range(M+1) + ] + for i in range(1, M+1): + for j in range(1, N+1): + R[i][j] = R[i][j-1] + 1 if grid[i-1][j-1] == 1 else 0 + C[i][j] = C[i-1][j] + 1 if grid[i-1][j-1] == 1 else 0 + + maxa = 0 + # looking at point i, j + for i in range(M): + for j in range(N): + for l in range(1, min(M, N)+1): + left = j - l + 1 + top = i - l + 1 + if left >= 0 and top >= 0: + # print(i, j, left, top) + if min(R[i+1][j+1], R[top+1][j+1], C[i+1][j+1], C[i+1][left+1]) >= l: + maxa = max(maxa, l) + + return maxa * maxa + \ No newline at end of file diff --git a/1219 Path with Maximum Gold.py b/1219 Path with Maximum Gold.py new file mode 100644 index 0000000..ea58044 --- /dev/null +++ b/1219 Path with Maximum Gold.py @@ -0,0 +1,112 @@ +""" +In a gold mine grid of size m x n, each cell in this mine has an integer representing the amount of gold in that cell, 0 if it is empty. + +Return the maximum amount of gold you can collect under the conditions: + +Every time you are located in a cell you will collect all the gold in that cell. +From your position, you can walk one step to the left, right, up, or down. +You can't visit the same cell more than once. +Never visit a cell with 0 gold. +You can start and stop collecting gold from any position in the grid that has some gold. + + +Example 1: + +Input: grid = [[0,6,0],[5,8,7],[0,9,0]] +Output: 24 +Explanation: +[[0,6,0], + [5,8,7], + [0,9,0]] +Path to get the maximum gold, 9 -> 8 -> 7. +Example 2: + +Input: grid = [[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]] +Output: 28 +Explanation: +[[1,0,7], + [2,0,6], + [3,4,5], + [0,3,0], + [9,0,20]] +Path to get the maximum gold, 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7. + + +Constraints: + +m == grid.length +n == grid[i].length +1 <= m, n <= 15 +0 <= grid[i][j] <= 100 +There are at most 25 cells containing gold. +""" +class Solution: + def getMaximumGold(self, grid: List[List[int]]) -> int: + """ + DFS + """ + self.grid = grid + self.M = len(grid) + self.N = len(grid[0]) + self.dirs = [(0, -1), (0, 1), (-1, 0), (1, 0)] + self.maxa = 0 + visited = [ + [False for _ in range(self.N)] + for _ in range(self.M) + ] + for i in range(self.M): + for j in range(self.N): + if grid[i][j] != 0: + self.dfs(i, j, 0, visited) + + return self.maxa + + def dfs(self, i, j, path_sum, visited): + path_sum += self.grid[i][j] + self.maxa = max(self.maxa, path_sum) + visited[i][j] = True + for di, dj in self.dirs: + I = i + di + J = j + dj + if 0 <= I < self.M and 0 <= J < self.N and self.grid[I][J] != 0 and not visited[I][J]: + self.dfs(I, J, path_sum, visited) + + visited[i][j] = False + path_sum -= self.grid[i][j] + + +class SolutionStyle: + def getMaximumGold(self, grid: List[List[int]]) -> int: + """ + DFS + """ + self.grid = grid + self.M = len(grid) + self.N = len(grid[0]) + self.dirs = [(0, -1), (0, 1), (-1, 0), (1, 0)] + self.maxa = 0 + visited = [ + [False for _ in range(self.N)] + for _ in range(self.M) + ] + for i in range(self.M): + for j in range(self.N): + self.dfs(i, j, 0, visited) + + return self.maxa + + def dfs(self, i, j, path_sum, visited): + if self.grid[i][j] == 0 or visited[i][j]: + return + + path_sum += self.grid[i][j] + self.maxa = max(self.maxa, path_sum) + visited[i][j] = True + for di, dj in self.dirs: + I = i + di + J = j + dj + if 0 <= I < self.M and 0 <= J < self.N: + self.dfs(I, J, path_sum, visited) + + visited[i][j] = False + path_sum -= self.grid[i][j] \ No newline at end of file diff --git a/1765 Map of Highest Peak.py b/1765 Map of Highest Peak.py new file mode 100644 index 0000000..3542062 --- /dev/null +++ b/1765 Map of Highest Peak.py @@ -0,0 +1,84 @@ +""" +You are given an integer matrix isWater of size m x n that represents a map of land and water cells. + +If isWater[i][j] == 0, cell (i, j) is a land cell. +If isWater[i][j] == 1, cell (i, j) is a water cell. +You must assign each cell a height in a way that follows these rules: + +The height of each cell must be non-negative. +If the cell is a water cell, its height must be 0. +Any two adjacent cells must have an absolute height difference of at most 1. A cell is adjacent to another cell if the former is directly north, east, south, or west of the latter (i.e., their sides are touching). +Find an assignment of heights such that the maximum height in the matrix is maximized. + +Return an integer matrix height of size m x n where height[i][j] is cell (i, j)'s height. If there are multiple solutions, return any of them. + + + +Example 1: + + + +Input: isWater = [[0,1],[0,0]] +Output: [[1,0],[2,1]] +Explanation: The image shows the assigned heights of each cell. +The blue cell is the water cell, and the green cells are the land cells. +Example 2: + + + +Input: isWater = [[0,0,1],[1,0,0],[0,0,0]] +Output: [[1,1,0],[0,1,1],[1,2,2]] +Explanation: A height of 2 is the maximum possible height of any assignment. +Any height assignment that has a maximum height of 2 while still meeting the rules will also be accepted. + + +Constraints: + +m == isWater.length +n == isWater[i].length +1 <= m, n <= 1000 +isWater[i][j] is 0 or 1. +There is at least one water cell. + + +Note: This question is the same as 542: https://leetcode.com/problems/01-matrix/ +""" +from collections import deque +import sys + + +class Solution: + def highestPeak(self, isWater: List[List[int]]) -> List[List[int]]: + """ + BFS + + not maximize height, but find the distance to water + """ + M = len(isWater) + N = len(isWater[0]) + q = deque() + dist = [ + [sys.maxsize for _ in range(N)] + for _ in range(M) + ] + for i in range(M): + for j in range(N): + if isWater[i][j] == 1: + q.append((i, j)) + dist[i][j] = 0 + + + dirs = [(0, -1), (0, 1), (-1, 0), (1, 0)] + while q: + i, j = q.popleft() + for di, dj in dirs: + I = i + di + J = j + dj + if 0 <= I < M and 0 <= J < N and isWater[I][J] == 0: + d = dist[i][j] + 1 + if d < dist[I][J]: + dist[I][J] = d + q.append((I, J)) + + return dist + diff --git a/835 Image Overlap.py b/835 Image Overlap.py new file mode 100644 index 0000000..902802a --- /dev/null +++ b/835 Image Overlap.py @@ -0,0 +1,67 @@ +""" +You are given two images, img1 and img2, represented as binary, square matrices of size n x n. A binary matrix has only 0s and 1s as values. + +We translate one image however we choose by sliding all the 1 bits left, right, up, and/or down any number of units. We then place it on top of the other image. We can then calculate the overlap by counting the number of positions that have a 1 in both images. + +Note also that a translation does not include any kind of rotation. Any 1 bits that are translated outside of the matrix borders are erased. + +Return the largest possible overlap. + + + +Example 1: + + +Input: img1 = [[1,1,0],[0,1,0],[0,1,0]], img2 = [[0,0,0],[0,1,1],[0,0,1]] +Output: 3 +Explanation: We translate img1 to right by 1 unit and down by 1 unit. + +The number of positions that have a 1 in both images is 3 (shown in red). + +Example 2: + +Input: img1 = [[1]], img2 = [[1]] +Output: 1 +Example 3: + +Input: img1 = [[0]], img2 = [[0]] +Output: 0 + + +Constraints: + +n == img1.length == img1[i].length +n == img2.length == img2[i].length +1 <= n <= 30 +img1[i][j] is either 0 or 1. +img2[i][j] is either 0 or 1. +""" +class Solution: + def largestOverlap(self, img1: List[List[int]], img2: List[List[int]]) -> int: + """ + sliding - 4 dirs any offset + sliding outside - the relative position doesn't change + + brute force: starting from any position of img1, and img2, dfs + O(N^2 * N^2 * N) + + better brute force: + Chekc Overlap O(N^2) + Offset - sliding 2N horizontailly, 2N vertically + """ + M = len(img1) + N = len(img1[0]) + maxa = 0 + for di in range(-M+1, M): + for dj in range(-N+1, N): + cur = 0 + for i in range(M): + for j in range(N): + I = i + di + J = j + dj + if 0 <= I < M and 0 <= J < N: + if img1[I][J] == img2[i][j] and img1[I][J] == 1: + cur += 1 + maxa = max(maxa, cur) + + return maxa \ No newline at end of file diff --git a/840 Magic Squares In Grid.py b/840 Magic Squares In Grid.py new file mode 100644 index 0000000..b7b35bd --- /dev/null +++ b/840 Magic Squares In Grid.py @@ -0,0 +1,90 @@ +""" +A 3 x 3 magic square is a 3 x 3 grid filled with distinct numbers from 1 to 9 such that each row, column, and both diagonals all have the same sum. + +Given a row x col grid of integers, how many 3 x 3 magic square subgrids are there? + +Note: while a magic square can only contain numbers from 1 to 9, grid may contain numbers up to 15. + + + +Example 1: + + +Input: grid = [[4,3,8,4],[9,5,1,9],[2,7,6,2]] +Output: 1 +Explanation: +The following subgrid is a 3 x 3 magic square: + +while this one is not: + +In total, there is only one magic square inside the given grid. +Example 2: + +Input: grid = [[8]] +Output: 0 + + +Constraints: + +row == grid.length +col == grid[i].length +1 <= row, col <= 10 +0 <= grid[i][j] <= 15 +""" +class Solution: + def numMagicSquaresInside(self, grid: List[List[int]]) -> int: + """ + brute force O(N^2) * O(N^2) + + sliding submatrix + + O(N^2) * O(N) + """ + M = len(grid) + N = len(grid[0]) + + magics = [ + [ + [4, 9, 2], + [3, 5, 7], + [8, 1, 6]], + [ + [2, 7, 6], + [9, 5, 1], + [4, 3, 8]], + [ + [6, 1, 8], + [7, 5, 3], + [2, 9, 4]], + [ + [8, 3, 4], + [1, 5, 9], + [6, 7, 2]], + [ + [4, 3, 8], + [9, 5, 1], + [2, 7, 6]], + [ + [2, 9, 4], + [7, 5, 3], + [6, 1, 8]], + [ + [6, 7, 2], + [1, 5, 9], + [8, 3, 4]], + [ + [8, 1, 6], + [3, 5, 7], + [4, 9, 2]] + ] + + cnt = 0 + for r in range(M - 2): + for c in range(N - 2): + subgrid = [ + grid[r + i][c:c + 3] + for i in range(3) + ] + if subgrid in magics: + cnt += 1 + return cnt pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy