From 901d31d5cc92fc88e8ff468f5d8551cb60dc96bd Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 19 Aug 2015 23:46:28 +0800 Subject: [PATCH 001/585] init --- 261 Graph Valid Tree.py | 13 +++++++++++++ 263 Ugly Number.py | 18 ++++++++++++++++++ 264 Ugly Number II.py | 17 +++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 261 Graph Valid Tree.py create mode 100644 263 Ugly Number.py create mode 100644 264 Ugly Number II.py diff --git a/261 Graph Valid Tree.py b/261 Graph Valid Tree.py new file mode 100644 index 0000000..e2914db --- /dev/null +++ b/261 Graph Valid Tree.py @@ -0,0 +1,13 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution: + def validTree(self, n, edges): + """ + :type n: int + :edges: List[List[int] + :rtype: bool + """ \ No newline at end of file diff --git a/263 Ugly Number.py b/263 Ugly Number.py new file mode 100644 index 0000000..d99b82a --- /dev/null +++ b/263 Ugly Number.py @@ -0,0 +1,18 @@ +""" +Write a program to check whether a given number is an ugly number. + +Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. For example, 6, 8 are ugly while 14 is not +ugly since it includes another prime factor 7. + +Note that 1 is typically treated as an ugly number. +""" +__author__ = 'Daniel' + + +class Solution: + def isUgly(self, num): + """ + + :type num: int + :rtype: bool + """ \ No newline at end of file diff --git a/264 Ugly Number II.py b/264 Ugly Number II.py new file mode 100644 index 0000000..a64b70b --- /dev/null +++ b/264 Ugly Number II.py @@ -0,0 +1,17 @@ +""" +Write a program to find the n-th ugly number. + +Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. For example, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 +is the sequence of the first 10 ugly numbers. + +Note that 1 is typically treated as an ugly number. +""" +__author__ = 'Daniel' + + +class Solution: + def nthUglyNumber(self, n): + """ + :type n: int + :rtype: int + """ \ No newline at end of file From 896cbf6303439682a1657b3378a25babfdac9b69 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 23 Aug 2015 23:48:17 +0800 Subject: [PATCH 002/585] graph --- 261 Graph Valid Tree.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/261 Graph Valid Tree.py b/261 Graph Valid Tree.py index e2914db..7d12a02 100644 --- a/261 Graph Valid Tree.py +++ b/261 Graph Valid Tree.py @@ -1,13 +1,43 @@ """ Premium Question """ +from collections import defaultdict + __author__ = 'Daniel' -class Solution: +class Solution(object): def validTree(self, n, edges): """ :type n: int :edges: List[List[int] :rtype: bool - """ \ No newline at end of file + """ + if not edges: + return n in (0, 1) + + V = defaultdict(list) + for edge in edges: + V[edge[0]].append(edge[1]) + V[edge[1]].append(edge[0]) + + visited = set() + marked = set() + if not self.dfs(V, edges[0][0], None, visited, marked): + return False + + return len(visited) == n + + def dfs(self, V, k, pi, visited, marked): + if k in marked: + return False + + marked.add(k) + for neighbor in V[k]: + if neighbor != pi: + if not self.dfs(V, neighbor, k, visited, marked): + return False + + marked.remove(k) + visited.add(k) + return True \ No newline at end of file From 83ba9fde098d2572a6b6197cbba9874a472211e8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 24 Aug 2015 00:27:41 +0800 Subject: [PATCH 003/585] prime factor --- 263 Ugly Number.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/263 Ugly Number.py b/263 Ugly Number.py index d99b82a..d449b25 100644 --- a/263 Ugly Number.py +++ b/263 Ugly Number.py @@ -9,10 +9,31 @@ __author__ = 'Daniel' -class Solution: +class Solution(object): def isUgly(self, num): """ + Prime factor :type num: int :rtype: bool - """ \ No newline at end of file + """ + if num < 1: + return False + if num == 1: + return True + + ugly = {2, 3, 5} + + prime = 2 + while prime*prime <= num and num > 1: + if num % prime != 0: + prime += 1 + else: + num /= prime + if prime not in ugly: + return False + + if num not in ugly: + return False + + return True \ No newline at end of file From 9a2458523dee338db26b892c13be2a637b49af30 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 24 Aug 2015 00:56:20 +0800 Subject: [PATCH 004/585] heap --- 264 Ugly Number II.py | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/264 Ugly Number II.py b/264 Ugly Number II.py index a64b70b..fcf6443 100644 --- a/264 Ugly Number II.py +++ b/264 Ugly Number II.py @@ -6,12 +6,51 @@ Note that 1 is typically treated as an ugly number. """ +import heapq + + __author__ = 'Daniel' -class Solution: +class Node(object): + def __init__(self, origin, q): + self.origin = origin + self.q = q + + def __cmp__(self, other): + return self.q[0] - other.q[0] + + +class Solution(object): def nthUglyNumber(self, n): """ + Heap :type n: int :rtype: int - """ \ No newline at end of file + """ + if n == 1: + return 1 + + n -= 1 # exclude 1 + + ugly = [2, 3, 5] + qs = [Node(i, [i]) for i in ugly] + h = list(qs) # shallow copy + + heapq.heapify(h) + + cnt = 0 + ret = 2 + while cnt < n: + cnt += 1 + popped = heapq.heappop(h) + ret = popped.q.pop(0) + for i in xrange(ugly.index(popped.origin), 3): + qs[i].q.append(ret*ugly[i]) + + heapq.heappush(h, popped) + + return ret + +if __name__ == "__main__": + assert Solution().nthUglyNumber(10) == 12 \ No newline at end of file From 53a2024e0429281c7c2d5ce59921990dd9c58279 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 26 Aug 2015 23:08:27 +0800 Subject: [PATCH 005/585] dp --- 265 Paint House II.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 265 Paint House II.py diff --git a/265 Paint House II.py b/265 Paint House II.py new file mode 100644 index 0000000..2b3f062 --- /dev/null +++ b/265 Paint House II.py @@ -0,0 +1,33 @@ +""" +Premium Question +""" +import sys + +__author__ = 'Daniel' + + +class Solution(object): + def minCostII(self, costs): + """ + Lef F[i][j] be the total min costs when the houses BEFORE i are painted, with (i-1)-th house pained as color j + :type costs: List[List[int]] + :rtype: int + """ + if not costs: + return 0 + + n = len(costs) + m = len(costs[0]) + F = [[0 for _ in xrange(m)] for _ in xrange(n+1)] + for i in xrange(1, n+1): + for k1 in xrange(m): + F[i][k1] = sys.maxint + for k0 in xrange(m): + if i == 1 or k1 != k0: + F[i][k1] = min(F[i][k1], F[i-1][k0]+costs[i-1][k1]) + + return min(F[n][i] for i in xrange(m)) + + +if __name__ == "__main__": + assert Solution().minCostII([[8]]) == 8 \ No newline at end of file From 679c40b8981e614fc67c19a987942d0571d4ce81 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 27 Aug 2015 23:54:36 +0800 Subject: [PATCH 006/585] array --- 268 Missing Number.py | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 268 Missing Number.py diff --git a/268 Missing Number.py b/268 Missing Number.py new file mode 100644 index 0000000..554a347 --- /dev/null +++ b/268 Missing Number.py @@ -0,0 +1,44 @@ +""" +iven an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is missing from the array. + +For example, +Given nums = [0, 1, 3] return 2. + +Note: +Your algorithm should run in linear runtime complexity. Could you implement it using only constant extra space +complexity? +""" +__author__ = 'Daniel' + + +class Solution(object): + def missingNumber(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + num_n = None + n = len(nums) + + i = 0 + while i < n: + if nums[i] == n: + num_n = nums[i] + nums[i] = None + i += 1 + + elif nums[i] is not None and nums[i] != i: + j = nums[i] + nums[i], nums[j] = nums[j], nums[i] + # nums[i], nums[nums[i]] = nums[nums[i]], nums[i] + + else: + i += 1 + + if not num_n: + return n + + return nums.index(None) + +if __name__ == "__main__": + assert Solution().missingNumber([2, 0]) == 1 From f33eb0bc9fcabb4cd673e6a4e8195c6f91f34dea Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 28 Aug 2015 00:10:05 +0800 Subject: [PATCH 007/585] doc --- 268 Missing Number.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/268 Missing Number.py b/268 Missing Number.py index 554a347..1ba9f83 100644 --- a/268 Missing Number.py +++ b/268 Missing Number.py @@ -1,5 +1,5 @@ """ -iven an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is missing from the array. +Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is missing from the array. For example, Given nums = [0, 1, 3] return 2. @@ -14,6 +14,13 @@ class Solution(object): def missingNumber(self, nums): """ + Algorithm: + Hashmap but use the array itself as the hashmap + + Notice: + nums[i], nums[nums[i]] = nums[nums[i]], nums[i] + Above is wrong, since evaluate from left to right; thus, when nums[nums[i]] on RHS is None, on LHS, nums[i] is + eval to None and nums[nums[i]] indexes by None, causing errors. :type nums: List[int] :rtype: int """ @@ -23,14 +30,13 @@ def missingNumber(self, nums): i = 0 while i < n: if nums[i] == n: - num_n = nums[i] - nums[i] = None - i += 1 + num_n = nums[i] + nums[i] = None + i += 1 elif nums[i] is not None and nums[i] != i: j = nums[i] nums[i], nums[j] = nums[j], nums[i] - # nums[i], nums[nums[i]] = nums[nums[i]], nums[i] else: i += 1 @@ -40,5 +46,6 @@ def missingNumber(self, nums): return nums.index(None) + if __name__ == "__main__": assert Solution().missingNumber([2, 0]) == 1 From 1d0ff35ded055c7f1c0262a0067c9de366cbe5b6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 28 Aug 2015 00:13:14 +0800 Subject: [PATCH 008/585] doc --- 268 Missing Number.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/268 Missing Number.py b/268 Missing Number.py index 1ba9f83..0c2ac20 100644 --- a/268 Missing Number.py +++ b/268 Missing Number.py @@ -14,13 +14,21 @@ class Solution(object): def missingNumber(self, nums): """ - Algorithm: + Algorithm: Hashmap but use the array itself as the hashmap Notice: - nums[i], nums[nums[i]] = nums[nums[i]], nums[i] + nums[i], nums[nums[i]] = nums[nums[i]], nums[i] Above is wrong, since evaluate from left to right; thus, when nums[nums[i]] on RHS is None, on LHS, nums[i] is eval to None and nums[nums[i]] indexes by None, causing errors. + + Alternatively, it is correct that: + nums[nums[i]], nums[i]= nums[i], nums[nums[i]] + + To be safe, just use: + j = nums[i] + nums[i], nums[j] = nums[j], nums[i] + :type nums: List[int] :rtype: int """ From e24e0a906383cf496db51ca84d10eafa15e43ed1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 28 Aug 2015 01:15:47 +0800 Subject: [PATCH 009/585] map --- 266 Palindrome Permutation.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 266 Palindrome Permutation.py diff --git a/266 Palindrome Permutation.py b/266 Palindrome Permutation.py new file mode 100644 index 0000000..63092cb --- /dev/null +++ b/266 Palindrome Permutation.py @@ -0,0 +1,26 @@ +""" +Premium Question +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class Solution(object): + def canPermutePalindrome(self, s): + """ + :type s: str + :rtype: bool + """ + m = defaultdict(int) + for c in s: + m[c] += 1 + + once = False + for v in m.values(): + if v % 2 == 1: + if once: + return False + once = True + + return True \ No newline at end of file From 352b96d6792360d94fd1ace0e39e87bdf0a693fe Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 28 Aug 2015 01:53:44 +0800 Subject: [PATCH 010/585] backtracking --- 267 Palindrome Permutation II.py | 57 ++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 267 Palindrome Permutation II.py diff --git a/267 Palindrome Permutation II.py b/267 Palindrome Permutation II.py new file mode 100644 index 0000000..eab8ab5 --- /dev/null +++ b/267 Palindrome Permutation II.py @@ -0,0 +1,57 @@ +""" +Given a string s, return all the palindromic permutations (without duplicates) of it. Return an empty list if no palindromic permutation could be form. + +For example: + +Given s = "aabb", return ["abba", "baab"]. + +Given s = "abc", return []. +""" +from collections import defaultdict + + +__author__ = 'Daniel' + + +class Solution(object): + def generatePalindromes(self, s): + """ + :type s: str + :rtype: List[str] + """ + m = defaultdict(int) + for c in s: + m[c] += 1 + + odd = None + for k, v in m.items(): + if v % 2 == 1: + if odd is not None: + return [] + odd = k + + cur = "" + if odd: + m[odd] -= 1 + cur = odd + + ret = [] + # actually only need to build half + self.grow(s, m, None, cur, ret) + return ret + + def grow(self, s, m, pi, cur, ret): + if len(cur) == len(s): + ret.append(cur) + return + + for k in m.keys(): + if k != pi and m[k] > 0: + for i in xrange(1, m[k]/2+1): + m[k] -= i*2 + self.grow(s, m, k, k*i+cur+k*i, ret) + m[k] += i*2 + + +if __name__ == "__main__": + print Solution().generatePalindromes("aabb") \ No newline at end of file From 25b80bdf7cc20c895b5fdda2ccd82a7475f8d71e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 28 Aug 2015 02:20:18 +0800 Subject: [PATCH 011/585] bst --- 270 Closest Binary Search Tree Value.py | 52 +++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 270 Closest Binary Search Tree Value.py diff --git a/270 Closest Binary Search Tree Value.py b/270 Closest Binary Search Tree Value.py new file mode 100644 index 0000000..26fcdea --- /dev/null +++ b/270 Closest Binary Search Tree Value.py @@ -0,0 +1,52 @@ +""" +Premium Question +""" +import sys + +__author__ = 'Daniel' + + +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def closestValue(self, root, target): + """ + :type root: TreeNode + :type target: float + :rtype: int + """ + lower = [-sys.float_info.max] + self.find(root, target, lower, True) + higher = [sys.float_info.max] + self.find(root, target, higher, False) + if higher[0] - target < target - lower[0]: + return int(higher[0]) + else: + return int(lower[0]) + + def find(self, root, target, ret, lower=True): + if not root: + return + + if root.val == target: + ret[0] = root.val + return + + if root.val < target: + if lower: + ret[0] = max(ret[0], root.val) + + self.find(root.right, target, ret, lower) + else: + if not lower: + ret[0] = min(ret[0], root.val) + + self.find(root.left, target, ret, lower) + +if __name__ == "__main__": + assert Solution().closestValue(TreeNode(2147483647), 0.0) == 2147483647 From f236920411b2a21d26dc14fc59b13746a2176cc5 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 31 Aug 2015 23:37:05 +0800 Subject: [PATCH 012/585] topological sort --- 269 Alien Dictionary.py | 73 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 269 Alien Dictionary.py diff --git a/269 Alien Dictionary.py b/269 Alien Dictionary.py new file mode 100644 index 0000000..1d351c1 --- /dev/null +++ b/269 Alien Dictionary.py @@ -0,0 +1,73 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution(object): + def alienOrder(self, words): + """ + :type words: List[str] + :rtype: str + """ + V = {} + self.construct_graph(words, 0, len(words), 0, V) + visited = set() + marked = set() + ret = [] + + for k in V.keys(): + if k not in visited: + if not self.topoligical_dfs(V, k, visited, marked, ret): + return "" + + return "".join(reversed(ret)) + + + def construct_graph(self, words, up, down, ptr, V): + """ + :param words: + :param up: upper bound + :param down: lower bound + 1 + :param ptr: starting index for the char in the word + :param V: Vertices + :return: whether contains cycles + """ + i = up + while i < down: + if ptr >= len(words[i]): + i += 1 + else: + if words[i][ptr] not in V: + V[words[i][ptr]] = [] + + j = i+1 + while j < down and ptr < len(words[j]) and words[j][ptr] == words[i][ptr]: + j += 1 + + self.construct_graph(words, i, j, ptr+1, V) + if j < down and ptr < len(words[j]): + V[words[i][ptr]].append(words[j][ptr]) + + i = j + + def topoligical_dfs(self, V, cur, visited, marked, ret): + if cur in marked: + return False + + marked.add(cur) + for nei in V[cur]: + if nei not in visited: + if not self.topoligical_dfs(V, nei, visited, marked, ret): + return False + + marked.remove(cur) + visited.add(cur) + ret.append(cur) + 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" \ No newline at end of file From 6444957898e096d643cb28fd5d17680d883e169f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 1 Sep 2015 14:06:56 +0800 Subject: [PATCH 013/585] fix doc --- 269 Alien Dictionary.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/269 Alien Dictionary.py b/269 Alien Dictionary.py index 1d351c1..b9ef473 100644 --- a/269 Alien Dictionary.py +++ b/269 Alien Dictionary.py @@ -18,12 +18,11 @@ def alienOrder(self, words): for k in V.keys(): if k not in visited: - if not self.topoligical_dfs(V, k, visited, marked, ret): + if not self.topological_dfs(V, k, visited, marked, ret): return "" return "".join(reversed(ret)) - def construct_graph(self, words, up, down, ptr, V): """ :param words: @@ -31,7 +30,7 @@ def construct_graph(self, words, up, down, ptr, V): :param down: lower bound + 1 :param ptr: starting index for the char in the word :param V: Vertices - :return: whether contains cycles + :return: None """ i = up while i < down: @@ -51,14 +50,22 @@ def construct_graph(self, words, up, down, ptr, V): i = j - def topoligical_dfs(self, V, cur, visited, marked, ret): + def topological_dfs(self, V, cur, visited, marked, ret): + """ + :param V: Vertices HashMap + :param cur: currently visiting letter + :param visited: visited letters + :param marked: marked predecessor in the path + :param ret: the path, ordered topologically + :return: whether contains cycles + """ if cur in marked: return False marked.add(cur) for nei in V[cur]: if nei not in visited: - if not self.topoligical_dfs(V, nei, visited, marked, ret): + if not self.topological_dfs(V, nei, visited, marked, ret): return False marked.remove(cur) From 280eec589edc5fd32d4bd6623b04490e5011c6fc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 3 Sep 2015 00:00:06 +0800 Subject: [PATCH 014/585] init --- 271 Encode and Decode Strings.py | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 271 Encode and Decode Strings.py diff --git a/271 Encode and Decode Strings.py b/271 Encode and Decode Strings.py new file mode 100644 index 0000000..520a51f --- /dev/null +++ b/271 Encode and Decode Strings.py @@ -0,0 +1,34 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Codec(object): + def encode(self, strs): + """ + Encodes a list of strings to a single string. + + :type strs: List[str] + :rtype: str + """ + strs = map(lambda x: x.replace("\x00", "\\x00"), strs) + ret = "" + for s in strs: + ret += s+"\x00" + return ret + + def decode(self, s): + """ + Decodes a single string to a list of strings. + + :type s: str + :rtype: List[str] + """ + if "\x00" not in s: + return [] + + s = s[:-1] # traiing \x00 + strs = s.split("\x00") + strs = map(lambda x: x.replace("\\x00", "\x00"), strs) + return strs From 9f71d714a3cefdf203e6c2aaa4051eeaf79ab341 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 3 Sep 2015 00:41:13 +0800 Subject: [PATCH 015/585] update 2 algorithms --- 271 Encode and Decode Strings.py | 58 ++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/271 Encode and Decode Strings.py b/271 Encode and Decode Strings.py index 520a51f..40413d2 100644 --- a/271 Encode and Decode Strings.py +++ b/271 Encode and Decode Strings.py @@ -9,6 +9,64 @@ def encode(self, strs): """ Encodes a list of strings to a single string. + Algorithm: Length info + + :type strs: List[str] + :rtype: str + """ + strs = map(lambda x: str(len(x))+"/"+x, strs) + return reduce(lambda x, y: x+y, strs, "") # i.e. "".join(strs) + + def decode(self, s): + """ + Decodes a single string to a list of strings. + + :type s: str + :rtype: List[str] + """ + strs = [] + i = 0 + while i < len(s): + j = s.index("/", i) + l = int(s[i:j]) + strs.append(s[j+1:j+1+l]) + i = j+1+l + + return strs + + +class Codec(object): + def encode(self, strs): + """ + Encodes a list of strings to a single string. + + Algorithm: Escape + + :type strs: List[str] + :rtype: str + """ + strs = map(lambda x: x.replace("\n", "\n\n")+"_\n_", strs) + return reduce(lambda x, y: x+y, strs, "") + + def decode(self, s): + """ + Decodes a single string to a list of strings. + + :type s: str + :rtype: List[str] + """ + strs = s.split("_\n_") + strs = strs[:-1] # clear the trailing delimiter + return map(lambda x: x.replace("\n\n", "\n"), strs) + + +class Codec_error(object): + def encode(self, strs): + """ + Encodes a list of strings to a single string. + + This algorithm contains bugs if \\x00 exits in the original string + :type strs: List[str] :rtype: str """ From 6bd9dddb62ec30b6ff700b2c6c317c6e5e8f8440 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 4 Sep 2015 23:22:52 +0800 Subject: [PATCH 016/585] space --- 271 Encode and Decode Strings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/271 Encode and Decode Strings.py b/271 Encode and Decode Strings.py index 40413d2..8e01c1f 100644 --- a/271 Encode and Decode Strings.py +++ b/271 Encode and Decode Strings.py @@ -65,7 +65,7 @@ def encode(self, strs): """ Encodes a list of strings to a single string. - This algorithm contains bugs if \\x00 exits in the original string + This algorithm contains bugs if \\x00 exits in the original string :type strs: List[str] :rtype: str From 614d875aa82950db3dfe25c53a6814c1dc8ffa1c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 5 Sep 2015 23:24:06 +0800 Subject: [PATCH 017/585] doc --- 233 Number of Digit One.py | 6 +++++- 234 Palindrome Linked List.py | 4 ++-- 239 Sliding Window Maximum.py | 17 +++++++++-------- 240 Search a 2D Matrix II.py | 3 ++- 241 Different Ways to Add Parentheses.py | 3 ++- 244 Shortest Word Distance II.py | 6 ++---- 245 Shortest Word Distance III.py | 4 ++-- 257 Binary Tree Paths.py | 11 +++++++++++ 264 Ugly Number II.py | 5 ++++- 9 files changed, 39 insertions(+), 20 deletions(-) diff --git a/233 Number of Digit One.py b/233 Number of Digit One.py index ed0dae4..2aa9f67 100644 --- a/233 Number of Digit One.py +++ b/233 Number of Digit One.py @@ -11,6 +11,10 @@ class Solution: def countDigitOne(self, n): """ + Count the 1 occurrences at the digit i, due to: + 1. the digits higher than the currently counting digits + 2. the digits lower than the currently counting digits + Divide the question into smaller parts, count appearance at 1LSD, 2LSD, 3LSD respectively. :type n: int :rtype: int @@ -36,4 +40,4 @@ def countDigitOne(self, n): sig = temp - return cnt \ No newline at end of file + return cnt diff --git a/234 Palindrome Linked List.py b/234 Palindrome Linked List.py index 58f581e..44c4fed 100644 --- a/234 Palindrome Linked List.py +++ b/234 Palindrome Linked List.py @@ -18,7 +18,7 @@ def isPalindrome(self, head): """ Algorithms: 1. put into array O(N) - 2. midpoint, reverse the other + 2. midpoint, reverse the other - O(n) time and O(1) space :type head: ListNode :rtype: bool @@ -68,4 +68,4 @@ def reverse(self, head): cur.next = pre pre, cur = cur, nxt if head: head.next = None - return pre \ No newline at end of file + return pre diff --git a/239 Sliding Window Maximum.py b/239 Sliding Window Maximum.py index db2e044..0fd2bf8 100644 --- a/239 Sliding Window Maximum.py +++ b/239 Sliding Window Maximum.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -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 +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. For example, @@ -16,7 +16,7 @@ 1 3 -1 -3 5 [3 6 7] 7 Therefore, return the max sliding window as [3,3,5,5,6,7]. -Note: +Note: You may assume k is always valid, ie: 1 ≤ k ≤ input array's size for non-empty array. Follow up: @@ -32,6 +32,13 @@ def maxSlidingWindow(self, nums, k): 1. brute force 2. heap with lazy deletion 3. queue + + In the double-ended queue algorithm, you need to assign the queue with a meaning, in a way that you can + access the window's max in O(1). + + Invariant: the queue is storing non-decreasing-ordered elements of current window. + + The queue stores the index rather than element :type nums: list[] :type k: int :rtype: list[] @@ -49,9 +56,3 @@ def maxSlidingWindow(self, nums, k): ret.append(nums[q[0]]) return ret - - - - - - diff --git a/240 Search a 2D Matrix II.py b/240 Search a 2D Matrix II.py index a1f2503..978b23d 100644 --- a/240 Search a 2D Matrix II.py +++ b/240 Search a 2D Matrix II.py @@ -24,6 +24,7 @@ class Solution: def searchMatrix(self, matrix, target): """ + Multiple round of binary search :type matrix: list[int][int] :type target: int @@ -63,4 +64,4 @@ def bisect(self, A, t, lower=True): return lo if __name__ == "__main__": - assert Solution().searchMatrix([[1, 4], [2, 5]], 4) == True \ No newline at end of file + assert Solution().searchMatrix([[1, 4], [2, 5]], 4) == True diff --git a/241 Different Ways to Add Parentheses.py b/241 Different Ways to Add Parentheses.py index 836943e..6e2ba0f 100644 --- a/241 Different Ways to Add Parentheses.py +++ b/241 Different Ways to Add Parentheses.py @@ -29,6 +29,7 @@ class Solution: def diffWaysToCompute(self, input): """ + Looping + Divide & Conquer :type: input :rtype: list[] @@ -63,4 +64,4 @@ def _eval(self, a, b, op): if __name__ == "__main__": - assert Solution().diffWaysToCompute("1+1") == [2] \ No newline at end of file + assert Solution().diffWaysToCompute("1+1") == [2] diff --git a/244 Shortest Word Distance II.py b/244 Shortest Word Distance II.py index d9662fa..d65e3e9 100644 --- a/244 Shortest Word Distance II.py +++ b/244 Shortest Word Distance II.py @@ -8,9 +8,7 @@ __author__ = 'Daniel' -class WordDistance: - # - # @param {string[]} words +class WordDistance(object): def __init__(self, words): """ initialize your data structure here. @@ -36,4 +34,4 @@ def shortest(self, word1, word2): if 0 <= idx+nei < len(self.word_dict[word2]): mini = min(mini, abs(i-self.word_dict[word2][idx+nei])) - return mini \ No newline at end of file + return mini diff --git a/245 Shortest Word Distance III.py b/245 Shortest Word Distance III.py index e854555..f39cd72 100644 --- a/245 Shortest Word Distance III.py +++ b/245 Shortest Word Distance III.py @@ -7,7 +7,7 @@ __author__ = 'Daniel' -class Solution: +class Solution(object): def shortestWordDistance(self, words, word1, word2): """ :type words: list[str] @@ -24,4 +24,4 @@ def shortestWordDistance(self, words, word1, word2): if 0 <= idx+nei < len(pos_lst2) and pos != pos_lst2[idx+nei]: mini = min(mini, abs(pos-pos_lst2[idx+nei])) - return mini \ No newline at end of file + return mini diff --git a/257 Binary Tree Paths.py b/257 Binary Tree Paths.py index 2804f31..41f47cf 100644 --- a/257 Binary Tree Paths.py +++ b/257 Binary Tree Paths.py @@ -54,3 +54,14 @@ def dfs(self, cur, path, ret): self.dfs(cur.right, path, ret) path.pop() # pop the shared path + def dfs_path(self, cur, path, ret): + if not cur: + return + + path.append(cur) + if not cur.left and not cur.right: + ret.append("->".join(map(lambda x: str(x.val), path))) + + self.dfs_path(cur.left, path, ret) + self.dfs_path(cur.right, path, ret) + path.pop() diff --git a/264 Ugly Number II.py b/264 Ugly Number II.py index fcf6443..2d76783 100644 --- a/264 Ugly Number II.py +++ b/264 Ugly Number II.py @@ -13,6 +13,9 @@ class Node(object): + """ + Data structure is key + """ def __init__(self, origin, q): self.origin = origin self.q = q @@ -53,4 +56,4 @@ def nthUglyNumber(self, n): return ret if __name__ == "__main__": - assert Solution().nthUglyNumber(10) == 12 \ No newline at end of file + assert Solution().nthUglyNumber(10) == 12 From 6ba3d4396357402fbbe39e4d44e4aacba81dd44f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 6 Sep 2015 23:47:49 -0700 Subject: [PATCH 018/585] tree --- 272 Closest Binary Search Tree Value II.py | 60 ++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 272 Closest Binary Search Tree Value II.py diff --git a/272 Closest Binary Search Tree Value II.py b/272 Closest Binary Search Tree Value II.py new file mode 100644 index 0000000..be7013a --- /dev/null +++ b/272 Closest Binary Search Tree Value II.py @@ -0,0 +1,60 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def closestKValues(self, root, target, k): + """ + consider the predecessors and successors of the closest node to the target + Like merge in the merge sort, compare and pick the closest one to the target and put it to the result list + + Inorder traversal gives us sorted predecessors, whereas reverse-inorder traversal gives us sorted successors + + :type root: TreeNode + :type target: float + :type k: int + :rtype: List[int] + """ + pre = [] + suc = [] + self.predecessors(root, target, pre) + self.successors(root, target, suc) + return self.merge(target, k, pre, suc) + + def predecessors(self, root, target, stk): + if not root: + return + self.predecessors(root.left, target, stk) + if root.val <= target: + stk.append(root.val) + self.predecessors(root.right, target, stk) + + def successors(self, root, target, stk): + if not root: + return + self.successors(root.right, target, stk) + if root.val > target: + stk.append(root.val) + self.successors(root.left, target, stk) + + def merge(self, target, k, pre, suc): + ret = [] + while len(ret) < k: + if not pre: + ret.append(suc.pop()) + elif not suc: + ret.append(pre.pop()) + elif abs(pre[-1] - target) < abs(suc[-1] - target): + ret.append(pre.pop()) + else: + ret.append(suc.pop()) + return ret \ No newline at end of file From 86a8840bfe255a90754fbd26967378c978558f52 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 7 Sep 2015 23:18:28 -0700 Subject: [PATCH 019/585] doc --- 220 Contains Duplicate III.py | 3 ++- 250 Count Univalue Subtrees.py | 9 +++------ 251 Flatten 2D Vector.py | 5 ++--- 254 Factor Combinations.py | 6 ++++-- 255 Verify Preorder Sequence in Binary Search Tree.py | 7 ++++--- 268 Missing Number.py | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/220 Contains Duplicate III.py b/220 Contains Duplicate III.py index 884b78a..2cc72f8 100644 --- a/220 Contains Duplicate III.py +++ b/220 Contains Duplicate III.py @@ -2,9 +2,10 @@ Given an array of integers, find out whether there are two distinct indices i and j in the array such that the difference between nums[i] and nums[j] is at most t and the difference between i and j is at most k. """ -__author__ = 'Daniel' from collections import OrderedDict +__author__ = 'Daniel' + class Solution: def containsNearbyAlmostDuplicate(self, nums, k, t): diff --git a/250 Count Univalue Subtrees.py b/250 Count Univalue Subtrees.py index b245926..82f18e9 100644 --- a/250 Count Univalue Subtrees.py +++ b/250 Count Univalue Subtrees.py @@ -18,7 +18,6 @@ def __init__(self): def countUnivalSubtrees(self, root): """ - :type root: TreeNode :rtype: int """ @@ -34,9 +33,7 @@ def is_unival(self, cur): if (not is_left or not is_right or cur.left and cur.left.val != cur.val or cur.right and cur.right.val != cur.val): - is_uni = False + return False else: - is_uni = True - self.cnt += 1 - - return is_uni + self.cnt += 1 # for currently visiting node as the root of subtree. + return True diff --git a/251 Flatten 2D Vector.py b/251 Flatten 2D Vector.py index 31c5a59..410b283 100644 --- a/251 Flatten 2D Vector.py +++ b/251 Flatten 2D Vector.py @@ -7,7 +7,6 @@ class Vector2D: def __init__(self, vec2d): """ - :type vec2d: list[list[int]] :type: None """ @@ -29,7 +28,7 @@ def next(self): def hasNext(self): """ - + This function structures the two pointers. :rtype: bool """ # update @@ -37,4 +36,4 @@ def hasNext(self): self.i += 1 self.j = 0 - return self.i < len(self.vec2d) and self.j < len(self.vec2d[self.i]) \ No newline at end of file + return self.i < len(self.vec2d) and self.j < len(self.vec2d[self.i]) diff --git a/254 Factor Combinations.py b/254 Factor Combinations.py index 8d466b5..f1e85af 100644 --- a/254 Factor Combinations.py +++ b/254 Factor Combinations.py @@ -21,6 +21,8 @@ def dfs(self, cur, ret): """ 16 + The currently processing factor in stored in cur list as the last element + get factors of cur[-1] [16] [2, 8] @@ -35,7 +37,7 @@ def dfs(self, cur, ret): n = cur.pop() start = cur[-1] if cur else 2 for i in xrange(start, int(sqrt(n))+1): - if n%i == 0: + if n % i == 0: cur.append(i) cur.append(n/i) self.dfs(cur, ret) @@ -69,4 +71,4 @@ def dfs_TLE(self, n, cur, ret): if __name__ == "__main__": - print Solution().getFactors(16) \ No newline at end of file + print Solution().getFactors(16) diff --git a/255 Verify Preorder Sequence in Binary Search Tree.py b/255 Verify Preorder Sequence in Binary Search Tree.py index 13f3f0b..724a650 100644 --- a/255 Verify Preorder Sequence in Binary Search Tree.py +++ b/255 Verify Preorder Sequence in Binary Search Tree.py @@ -7,8 +7,10 @@ class Solution: def verifyPreorder(self, preorder): """ - - :type preorder:list[int] + * Draw a valid BST, and swap a pair of nodes to get an invalid BST. + * Get the preorder traversal of the 2 BSTs and compare and contrast them. + * For tree traversal, a stack or a queue is normally involved. + :type preorder:List[int] :rtype: bool """ left_finished = None @@ -27,4 +29,3 @@ def verifyPreorder(self, preorder): if __name__ == "__main__": preorder = [3, 5, 2, 1, 4, 7, 6, 9, 8, 10] assert Solution().verifyPreorder(preorder) == False - diff --git a/268 Missing Number.py b/268 Missing Number.py index 0c2ac20..63ba511 100644 --- a/268 Missing Number.py +++ b/268 Missing Number.py @@ -15,7 +15,7 @@ class Solution(object): def missingNumber(self, nums): """ Algorithm: - Hashmap but use the array itself as the hashmap + Hashmap, but to save space, use the array itself as the hashmap Notice: nums[i], nums[nums[i]] = nums[nums[i]], nums[i] From 0ce5d948578dfdc0ff39b993829d005f6bca6f21 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 8 Sep 2015 22:33:18 -0700 Subject: [PATCH 020/585] bisect --- 278 First Bad Version.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 278 First Bad Version.py diff --git a/278 First Bad Version.py b/278 First Bad Version.py new file mode 100644 index 0000000..10f1e03 --- /dev/null +++ b/278 First Bad Version.py @@ -0,0 +1,34 @@ +""" +You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of +your product fails the quality check. Since each version is developed based on the previous version, all the versions +after a bad version are also bad. + +Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following +ones to be bad. + +You are given an API bool isBadVersion(version) which will return whether version is bad. Implement a function to find +the first bad version. You should minimize the number of calls to the API. +""" +__author__ = 'Daniel' + + +def isBadVersion(version): + pass + + +class Solution(object): + def firstBadVersion(self, n): + """ + :param n: An integers. + :return: An integer which is the first bad version. + """ + l = 1 + h = n+1 + while l < h: + m = (l+h)/2 + if not isBadVersion(m): + l = m+1 + else: + h = m + + return l \ No newline at end of file From 744833fdf2673e3ad0dec580826814a8ba95eac4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 9 Sep 2015 23:49:48 -0700 Subject: [PATCH 021/585] TLE --- 279 Perfect Squares.py | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 279 Perfect Squares.py diff --git a/279 Perfect Squares.py b/279 Perfect Squares.py new file mode 100644 index 0000000..fdbba4a --- /dev/null +++ b/279 Perfect Squares.py @@ -0,0 +1,45 @@ +""" +Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum +to n. + +For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9. +""" +import math + +__author__ = 'Daniel' + + +class Solution(object): + def numSquares(self, n): + """ + DP + :type n: int + :rtype: int + """ + F = [i for i in xrange(n+1)] + for i in xrange(0, n+1): + for j in xrange(1, int(math.sqrt(n-i))+1): + if i+j*j <= n: + F[i+j*j] = min(F[i+j*j], F[i]+1) + else: + break + + return F[n] + + def numSquares_TLE(self, n): + """ + DP + :type n: int + :rtype: int + """ + F = [i for i in xrange(n+1)] + for i in xrange(1, n+1): + for j in xrange(1, int(math.sqrt(i))+1): + if i-j*j >= 0: + F[i] = min(F[i], F[i-j*j]+1) + + return F[n] + + +if __name__ == "__main__": + print Solution().numSquares(13) \ No newline at end of file From 8d93a18f6eadbd84456fca7678bbbf15976713ad Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 12 Sep 2015 23:42:20 -0700 Subject: [PATCH 022/585] bfs --- 279 Perfect Squares.py | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/279 Perfect Squares.py b/279 Perfect Squares.py index fdbba4a..29bf7df 100644 --- a/279 Perfect Squares.py +++ b/279 Perfect Squares.py @@ -12,19 +12,32 @@ class Solution(object): def numSquares(self, n): """ - DP + bfs :type n: int :rtype: int """ - F = [i for i in xrange(n+1)] - for i in xrange(0, n+1): - for j in xrange(1, int(math.sqrt(n-i))+1): - if i+j*j <= n: - F[i+j*j] = min(F[i+j*j], F[i]+1) - else: - break - - return F[n] + q = [0] + visited = [False for _ in xrange(n+1)] + + level = 0 + while q: + level += 1 + l = len(q) + for i in xrange(l): + for j in xrange(1, int(math.sqrt(n))+1): + nxt = q[i]+j*j + if nxt <= n and visited[nxt]: + continue + elif nxt < n: + visited[nxt] = True + q.append(nxt) + elif nxt == n: + return level + else: + break + q = q[l:] + + return None def numSquares_TLE(self, n): """ @@ -42,4 +55,4 @@ def numSquares_TLE(self, n): if __name__ == "__main__": - print Solution().numSquares(13) \ No newline at end of file + assert Solution().numSquares(6) == 3 \ No newline at end of file From 5aa1dbbf80033761ab7674e59214cc62d0c8c097 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 13 Sep 2015 00:21:31 -0700 Subject: [PATCH 023/585] math --- 273 Integer to English Words.py | 89 +++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 273 Integer to English Words.py diff --git a/273 Integer to English Words.py b/273 Integer to English Words.py new file mode 100644 index 0000000..a0ba49d --- /dev/null +++ b/273 Integer to English Words.py @@ -0,0 +1,89 @@ +""" +Convert a non-negative integer to its english words representation. Given input is guaranteed to be less than 2^31 - 1. + +For example, +123 -> "One Hundred Twenty Three" +12345 -> "Twelve Thousand Three Hundred Forty Five" +1234567 -> "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven" +""" +__author__ = 'Daniel' + + +class Solution(object): + def __init__(self): + self.m = { + 1: "One", + 2: "Two", + 3: "Three", + 4: "Four", + 5: "Five", + 6: "Six", + 7: "Seven", + 8: "Eight", + 9: "Nine", + 10: "Ten", + 11: "Eleven", + 12: "Twelve", + 13: "Thirteen", + 14: "Fourteen", + 15: "Fifteen", + 16: "Sixteen", + 17: "Seventeen", + 18: "Eighteen", + 19: "Nineteen", + 20: "Twenty", + 30: "Thirty", + 40: "Forty", + 50: "Fifty", + 60: "Sixty", + 70: "Seventy", + 80: "Eighty", + 90: "Ninety", + 100: "Hundred", + 1000: "Thousand", + 1000000: "Million", + 1000000000: "Billion" + + } + + def numberToWords(self, num): + """ + :type num: int + :rtype: str + """ + if num == 0: + return "Zero" + + ret = [] + self.toWords(num, ret) + return " ".join(map(str, ret)) + + def toWords(self, num, ret): + sigs = [1000000000, 1000000, 1000, 100] + for sig in sigs: + num = self.partial(num, sig, ret) + + ten = 10 + if num/ten > 1: + ret.append(self.m[num/ten*ten]) + if num%ten != 0: + ret.append(self.m[num%ten]) + elif num/ten == 1: + ret.append(self.m[num]) + elif num != 0: + ret.append(self.m[num]) + + def partial(self, num, sig, ret): + if num/sig: + pre = [] + self.toWords(num/sig, pre) + ret.extend(pre) + ret.append(self.m[sig]) + num %= sig + + return num + + +if __name__ == "__main__": + assert Solution().numberToWords(1234567891) == "One Billion Two Hundred Thirty Four Million Five Hundred Sixty " \ + "Seven Thousand Eight Hundred Ninety One" \ No newline at end of file From 2caf0862bd9fd58a2b7f726354bc675769d7484c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 14 Sep 2015 23:21:23 -0700 Subject: [PATCH 024/585] sort --- 280 Wiggle Sort.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 280 Wiggle Sort.py diff --git a/280 Wiggle Sort.py b/280 Wiggle Sort.py new file mode 100644 index 0000000..6022760 --- /dev/null +++ b/280 Wiggle Sort.py @@ -0,0 +1,19 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution(object): + def wiggleSort(self, nums): + """ + Solve by enumerating examples + :type nums: List[int] + :rtype: void Do not return anything, modify nums in-place instead. + """ + i = 0 + for elt in sorted(nums): + if i >= len(nums): + i = 1 + nums[i] = elt + i += 2 \ No newline at end of file From 915696aab2f52a59d2d481bc4880284b0f8b4fb4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 14 Sep 2015 23:56:21 -0700 Subject: [PATCH 025/585] sort h-index --- 274 H-Index.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 274 H-Index.py diff --git a/274 H-Index.py b/274 H-Index.py new file mode 100644 index 0000000..f0d203c --- /dev/null +++ b/274 H-Index.py @@ -0,0 +1,33 @@ +""" +Given an array of citations (each citation is a non-negative integer) of a researcher, write a function to compute the +researcher's h-index. + +According to the definition of h-index on Wikipedia: "A scientist has index h if h of his/her N papers have at least h +citations each, and the other N - h papers have no more than h citations each." + +For example, given citations = [3, 0, 6, 1, 5], which means the researcher has 5 papers in total and each of them had +received 3, 0, 6, 1, 5 citations respectively. Since the researcher has 3 papers with at least 3 citations each and the +remaining two with no more than 3 citations each, his h-index is 3. + +Note: If there are several possible values for h, the maximum one is taken as the h-index. +""" +__author__ = 'Daniel' + + +class Solution(object): + def hIndex(self, citations): + """ + Algorithm sort + :type citations: List[int] + :rtype: int + """ + citations.sort(reverse=True) + citations.append(0) + h = 0 + for i in xrange(len(citations)-1): + if citations[i] >= i+1 >= citations[i+1]: + h = i+1 + elif h: + break + + return h \ No newline at end of file From 4d33d51b77067938ab67420f1e6c0179555e988f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 15 Sep 2015 09:02:00 -0700 Subject: [PATCH 026/585] hash --- 274 H-Index.py | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/274 H-Index.py b/274 H-Index.py index f0d203c..f4d7521 100644 --- a/274 H-Index.py +++ b/274 H-Index.py @@ -16,6 +16,47 @@ class Solution(object): def hIndex(self, citations): + """ + Reverse mapping & DP + Determine the range of h-index + Chunk by n + Let F[i] be the number of paper with >= i citations + :type citations: List[int] + :rtype: int + """ + n = len(citations) + F = [0 for _ in xrange(n+1)] + for elt in citations: + if elt >= n: # chunk + F[n] += 1 + else: + F[elt] += 1 + + if F[n] >= n: + return n + + for i in xrange(n-1, -1, -1): + F[i] += F[i+1] + if F[i] >= i: + return i + + return 0 + + def hIndex_sort(self, citations): + """ + Algorithm forward sort + :type citations: List[int] + :rtype: int + """ + n = len(citations) + citations.sort() + for i in xrange(n): + if citations[i] >= n-i: + return n-i + + return 0 + + def hIndex_reverse_sort(self, citations): """ Algorithm sort :type citations: List[int] @@ -30,4 +71,7 @@ def hIndex(self, citations): elif h: break - return h \ No newline at end of file + return h + +if __name__ == "__main__": + print Solution().hIndex([3, 0, 6, 1, 5]) \ No newline at end of file From e403e7ba8ed419ffd66c319d09037ad759db2302 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 15 Sep 2015 11:55:02 -0700 Subject: [PATCH 027/585] bin search --- 274 H-Index.py | 4 ++-- 275 H-Index II.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 275 H-Index II.py diff --git a/274 H-Index.py b/274 H-Index.py index f4d7521..cdd0aff 100644 --- a/274 H-Index.py +++ b/274 H-Index.py @@ -18,7 +18,7 @@ class Solution(object): def hIndex(self, citations): """ Reverse mapping & DP - Determine the range of h-index + Determine the range of h-index Chunk by n Let F[i] be the number of paper with >= i citations :type citations: List[int] @@ -74,4 +74,4 @@ def hIndex_reverse_sort(self, citations): return h if __name__ == "__main__": - print Solution().hIndex([3, 0, 6, 1, 5]) \ No newline at end of file + assert Solution().hIndex([3, 0, 6, 1, 5]) == 3 \ No newline at end of file diff --git a/275 H-Index II.py b/275 H-Index II.py new file mode 100644 index 0000000..158de96 --- /dev/null +++ b/275 H-Index II.py @@ -0,0 +1,28 @@ +""" +Follow up for H-Index: What if the citations array is sorted in ascending order? Could you optimize your algorithm? +""" +__author__ = 'Daniel' + + +class Solution(object): + def hIndex(self, citations): + """ + From linear search into bin search + :type citations: List[int] + :rtype: int + """ + n = len(citations) + s = 0 + e = n + while s < e: + m = (s+e)/2 + if citations[m] >= n-m: + e = m + else: + s = m+1 + + return n-s + + +if __name__ == "__main__": + assert Solution().hIndex([0, 1, 3, 5, 6]) == 3 \ No newline at end of file From be6e4aed26eb02c2d75d47469b190fd7b322918d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 15 Sep 2015 18:37:37 -0700 Subject: [PATCH 028/585] dp --- 275 H-Index II.py | 2 +- 276 Paint Fence.py | 114 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 276 Paint Fence.py diff --git a/275 H-Index II.py b/275 H-Index II.py index 158de96..28b6b8c 100644 --- a/275 H-Index II.py +++ b/275 H-Index II.py @@ -7,7 +7,7 @@ class Solution(object): def hIndex(self, citations): """ - From linear search into bin search + From linear search into bin search :type citations: List[int] :rtype: int """ diff --git a/276 Paint Fence.py b/276 Paint Fence.py new file mode 100644 index 0000000..32f27cb --- /dev/null +++ b/276 Paint Fence.py @@ -0,0 +1,114 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution(object): + def numWays_oneliner(self, n, k): + return 0 if n < 1 else sum(reduce(lambda F, i: [(k-1)*(F[0]+F[1]), F[0]], xrange(1, n), [k, 0])) + + def numWays(self, n, k): + """ + Let F1[i] be the number of ways for A[:i] with last two with different colors + F2[i] be the number of ways for A[:i] with last two with same color + + F1[i] = (k-1)*(F1[i-1]+F2[i-1]) + F2[i] = F1[i-1] + + Optimize the space since only depends on i and i-1 + + :type n: int + :type k: int + :rtype: int + """ + if n < 1: + return 0 + + num_diff = k + num_same = 0 + for _ in xrange(1, n): + num_diff, num_same = (k-1)*(num_diff+num_same), num_diff + + return num_diff+num_same + + def numWays_MLE2(self, n, k): + """ + DP + Let F[i][j][l] be the number of ways of painting for A[:i] with A[i-1] as color j and A[i-2] as color l + :type n: int + :type k: int + :rtype: int + """ + if n < 1: + return 0 + + F = [[[0 for _ in xrange(k)] for _ in xrange(k)] for _ in xrange(2)] + EMPTY = 0 + + for j0 in xrange(k): + F[1][j0][EMPTY] = 1 + + for i in xrange(2, n+1): + for j0 in xrange(k): + for j1 in xrange(k): + F[i%2][j0][j1] = 0 + + for j0 in xrange(k): + for j1 in xrange(k): + for j2 in xrange(k): + if i == 2: + F[i%2][j0][j1] = F[(i-1)%2][j1][EMPTY] + + elif j1 == j2 and j0 != j1: + F[i%2][j0][j1] += F[(i-1)%2][j1][j2] + elif j1 != j2: + F[i%2][j0][j1] += F[(i-1)%2][j1][j2] + + ret = 0 + for j0 in xrange(k): + for j1 in xrange(k): + ret += F[n%2][j0][j1] + + return ret + + def numWays_MLE(self, n, k): + """ + DP + let F[i][j][l] be the number of ways of painting for A[:i] with A[i-1] as color j and A[i-2] as color l + :type n: int + :type k: int + :rtype: int + """ + if n < 1: + return 0 + + F = [[[0 for _ in xrange(k)] for _ in xrange(k)] for _ in xrange(n+1)] + EMPTY = 0 + + for j0 in xrange(k): + F[1][j0][EMPTY] = 1 + + for i in xrange(2, n+1): + for j0 in xrange(k): + for j1 in xrange(k): + for j2 in xrange(k): + if i == 2: + F[i][j0][j1] = F[i-1][j1][EMPTY] + + elif j1 == j2 and j0 != j1: + F[i][j0][j1] += F[i-1][j1][j2] + elif j1 != j2: + F[i][j0][j1] += F[i-1][j1][j2] + + ret = 0 + for j0 in xrange(k): + for j1 in xrange(k): + ret += F[n][j0][j1] + + return ret + +if __name__ == "__main__": + print Solution().numWays(3, 2) + + From 03336ee2670a793e1d34b71f6b5959993510ea1a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 16 Sep 2015 15:34:22 -0700 Subject: [PATCH 029/585] array --- 277 Find the Celebrity.py | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 277 Find the Celebrity.py diff --git a/277 Find the Celebrity.py b/277 Find the Celebrity.py new file mode 100644 index 0000000..b43ce82 --- /dev/null +++ b/277 Find the Celebrity.py @@ -0,0 +1,64 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +def knows(a, b): + """ + :param a: person + :param b: person + :return: whether person a knows person b + """ + + +class Solution(object): + def findCelebrity(self, n): + """ + :type n: int + :rtype: int + """ + i = 0 + j = n-1 + while i < j: + nxt_i, nxt_j = i, j + if knows(i, j) or not knows(j, i): + nxt_i += 1 + if knows(j, i) or not knows(i, j): + nxt_j -= 1 + i, j = nxt_i, nxt_j + + celebrity = i + for i in xrange(n): + if i != celebrity and (not knows(i, celebrity) or knows(celebrity, i)): + return -1 + + return celebrity + + def findCelebrity_set(self, n): + """ + O(n) + set + + :type n: int + :rtype: int + """ + V = set(range(n)) + + while len(V) > 1: + a = V.pop() + b = V.pop() + if knows(a, b) and not knows(b, a): + V.add(b) + elif not knows(a, b) and knows(b, a): + V.add(a) + + if not V: + return -1 + + celebrity = V.pop() + for i in xrange(n): + if i != celebrity and (not knows(i, celebrity) or knows(celebrity, i)): + return -1 + + return celebrity From f6632b0dee75977a5808b5d1751869ab7f8b8338 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 16 Sep 2015 16:42:18 -0700 Subject: [PATCH 030/585] itr --- 281 Zigzag Iterator.py | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 281 Zigzag Iterator.py diff --git a/281 Zigzag Iterator.py b/281 Zigzag Iterator.py new file mode 100644 index 0000000..4b68cfd --- /dev/null +++ b/281 Zigzag Iterator.py @@ -0,0 +1,55 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class ZigzagIterator(object): + def __init__(self, v1, v2): + """ + Initialize your data structure here. + :type v1: List[int] + :type v2: List[int] + """ + self.mat = [v1, v2] + self.maxa = max((c, r) for r, c in enumerate(map(lambda x: len(x)-1, self.mat))) + self.i = 0 + self.j = 0 + self._reposition() + + def _reposition(self): + while self.i >= len(self.mat) or self.j >= len(self.mat[self.i]): + if not self.hasNext(): + return + + elif self.i >= len(self.mat): + self.i = 0 + self.j += 1 + + elif self.j >= len(self.mat[self.i]): + self.i += 1 + + def next(self): + """ + :rtype: int + """ + if not self.hasNext(): + raise StopIteration + + ret = self.mat[self.i][self.j] + self.i += 1 + self._reposition() + return ret + + def hasNext(self): + """ + :rtype: bool + """ + return self.j <= self.maxa[0] + +if __name__ == "__main__": + v1 = [1, 2] + v2 = [3, 4, 5, 6] + itr = ZigzagIterator(v1, v2) + while itr.hasNext(): + print itr.next() \ No newline at end of file From 31aa84a43e63fd51c949fb957f9090a0ee91221e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 17 Sep 2015 16:19:19 -0700 Subject: [PATCH 031/585] dfs --- 246 Strobogrammatic Number.py | 1 - 247 Strobogrammatic Number II.py | 86 ++++++++++++++++++++++++++++++++ 281 Zigzag Iterator.py | 2 +- 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 247 Strobogrammatic Number II.py diff --git a/246 Strobogrammatic Number.py b/246 Strobogrammatic Number.py index dcd5143..81befed 100644 --- a/246 Strobogrammatic Number.py +++ b/246 Strobogrammatic Number.py @@ -14,7 +14,6 @@ def __init__(self): "0": "0" } - def isStrobogrammatic(self, num): """ diff --git a/247 Strobogrammatic Number II.py b/247 Strobogrammatic Number II.py new file mode 100644 index 0000000..7178779 --- /dev/null +++ b/247 Strobogrammatic Number II.py @@ -0,0 +1,86 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution(object): + def __init__(self): + self.map1 = ["11", "69", "88", "96", "00"] + + def findStrobogrammatic(self, n): + """ + :type n: int + :rtype: List[str] + """ + ret = [] + self.build(n, [], ret) + return ret + + def build(self, n, cur, ret): + if n%2 == 1 and len(cur) == 0: + for i in ["0", "1", "8"]: + cur.append(i) + self.build(n, cur, ret) + cur.pop() + return + + if len(cur)/2 == n/2: + ret.append("".join(cur)) + return + + for elt in self.map1: + if elt != "00" or len(cur) != n-2: + cur.insert(0, elt[0]) + cur.append(elt[1]) + self.build(n, cur, ret) + cur.pop() + cur.pop(0) + + +class SolutionOutputLimitExceeded(object): + def __init__(self): + self.map = { + "1": "1", + "6": "9", + "9": "6", + "8": "8", + "0": "0" + } + self.middle = ["1", "8", "0"] + + def findStrobogrammatic(self, n): + """ + :type n: int + :rtype: List[str] + """ + ret = [] + self.build(0, n, [], ret) + return ret + + def build(self, idx, n, cur, ret): + if idx == n/2: + if n % 2 != 0: + for m in self.middle: + if m != "0" or idx != 0: + temp = list(cur) + temp.append(m) + for i in xrange(idx-1, -1, -1): + temp.append(self.map[temp[i]]) + ret.append("".join(temp)) + else: + temp = list(cur) + for i in xrange(idx-1, -1, -1): + temp.append(self.map[temp[i]]) + ret.append("".join(temp)) + + return + + for k in self.map.keys(): + if k != "0" or idx != 0: + cur.append(k) + self.build(idx+1, n, cur, ret) + cur.pop() + +if __name__ == "__main__": + print Solution().findStrobogrammatic(1) \ No newline at end of file diff --git a/281 Zigzag Iterator.py b/281 Zigzag Iterator.py index 4b68cfd..0f23bb5 100644 --- a/281 Zigzag Iterator.py +++ b/281 Zigzag Iterator.py @@ -35,7 +35,7 @@ def next(self): """ if not self.hasNext(): raise StopIteration - + ret = self.mat[self.i][self.j] self.i += 1 self._reposition() From e3c6655ceb5cbbaa8410756d3ba3386d70520f5d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 19 Sep 2015 23:49:16 -0700 Subject: [PATCH 032/585] array --- 283 Move Zeroes.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 283 Move Zeroes.py diff --git a/283 Move Zeroes.py b/283 Move Zeroes.py new file mode 100644 index 0000000..ae8d48a --- /dev/null +++ b/283 Move Zeroes.py @@ -0,0 +1,35 @@ +""" +Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non- +zero elements. + +For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0]. + +Note: +You must do this in-place without making a copy of the array. +Minimize the total number of operations. +""" + +__author__ = 'Daniel' + + +class Solution(object): + def moveZeroes(self, nums): + """ + :type nums: List[int] + :rtype: void Do not return anything, modify nums in-place instead. + """ + cnt = 0 + i = 0 + for elt in nums: + if elt != 0: + nums[i] = elt + i += 1 + + for j in xrange(i, len(nums)): + nums[j] = 0 + + +if __name__ == "__main__": + lst = [0, 1, 0, 3, 12] + Solution().moveZeroes(lst) + assert lst == [1, 3, 12, 0, 0] \ No newline at end of file From 77d320421ed9a754bc964dcd45942db3443c1dab Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 20 Sep 2015 23:40:37 -0700 Subject: [PATCH 033/585] iterator --- 284 Peeking Iterator.py | 59 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 284 Peeking Iterator.py diff --git a/284 Peeking Iterator.py b/284 Peeking Iterator.py new file mode 100644 index 0000000..94c43b0 --- /dev/null +++ b/284 Peeking Iterator.py @@ -0,0 +1,59 @@ +""" +Given an Iterator class interface with methods: next() and hasNext(), design and implement a PeekingIterator that +support the peek() operation -- it essentially peek() at the element that will be returned by the next call to next(). +""" +__author__ = 'Daniel' + + +class Iterator(object): + def __init__(self, nums): + """ + Initializes an iterator object to the beginning of a list. + :type nums: List[int] + """ + + def hasNext(self): + """ + Returns true if the iteration has more elements. + :rtype: bool + """ + + def next(self): + """ + Returns the next element in the iteration. + :rtype: int + """ + + +class PeekingIterator(object): + def __init__(self, iterator): + """ + Initialize your data structure here. + :type iterator: Iterator + """ + self.nxt = None + self.iterator = iterator + + def peek(self): + """ + Returns the next element in the iteration without advancing the iterator. + :rtype: int + """ + if not self.nxt: + self.nxt = self.iterator.next() + + return self.nxt + + def next(self): + """ + :rtype: int + """ + ret = self.peek() + self.nxt = None + return ret + + def hasNext(self): + """ + :rtype: bool + """ + return self.nxt is not None or self.iterator.hasNext() From 3b26870e946b510797b6b284822a1011ce048fbe Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 21 Sep 2015 19:41:13 -0700 Subject: [PATCH 034/585] tree --- 002 Median of Two Sorted Arrays.py | 22 +++--- ... Substring Without Repeating Characters.py | 4 +- 004 Add Two Numbers.py | 13 ++-- 005 Longest Palindromic Substring.py | 27 +++---- 006 ZigZag Conversion.py | 9 ++- 007 Reverse Integer.py | 13 ++-- 008 String to Integer (atoi).py | 10 +-- 009 Palindrome Number.py | 17 ++--- 010 Container With Most Water.py | 8 ++- 011 Regular Expression Matching.py | 71 +++++++++---------- 012 Integer to Roman.py | 3 +- 013 Roman to Integer.py | 4 +- 014 3Sum.py | 31 ++++---- 156 Binary Tree Upside Down.py | 67 +++++++++++++++++ 283 Move Zeroes.py | 1 - 15 files changed, 192 insertions(+), 108 deletions(-) create mode 100644 156 Binary Tree Upside Down.py diff --git a/002 Median of Two Sorted Arrays.py b/002 Median of Two Sorted Arrays.py index 28b1d52..db560a1 100644 --- a/002 Median of Two Sorted Arrays.py +++ b/002 Median of Two Sorted Arrays.py @@ -3,6 +3,8 @@ run time complexity should be O(log (m+n)). """ __author__ = 'Danyang' + + class Solution: def findMedianSortedArrays(self, A, B): """ @@ -28,7 +30,7 @@ def findMedianSortedArrays(self, A, B): """ m = len(A) n = len(B) - if((m+n)&1==0): + if ((m+n)&1 == 0): return (self.find_kth(A, B, (m+n)/2)+self.find_kth(A, B, (m+n)/2-1))/2.0 else: return self.find_kth(A, B, (m+n)/2) @@ -46,27 +48,27 @@ def find_kth(self, A, B, k): if not B: return A[k] - if k==0: + if k == 0: return min(A[0], B[0]) m = len(A) n = len(B) # pay attention to consider the equal sign. Assigning equal sign is an art. - if A[m/2]>B[n/2]: - if k>m/2+n/2: + if A[m/2] > B[n/2]: + if k > m/2+n/2: return self.find_kth(A, B[n/2+1:], k-n/2-1) # exclude B[n/2] else: return self.find_kth(A[:m/2], B, k) # exclude A[m/2] else: - if k>m/2+n/2: + if k > m/2+n/2: return self.find_kth(A[m/2+1:], B, k-m/2-1) # exclude A[m/2] else: return self.find_kth(A, B[:n/2], k) # exclude B[n/2] -if __name__=="__main__": - assert Solution().findMedianSortedArrays([1,2], [1,2,3])==2 - assert Solution().findMedianSortedArrays([1, 2], [3])==2 - assert Solution().findMedianSortedArrays([1], [2, 3])==2 - assert Solution().findMedianSortedArrays([1, 2], [1, 2])==1.5 +if __name__ == "__main__": + assert Solution().findMedianSortedArrays([1, 2], [1, 2, 3]) == 2 + assert Solution().findMedianSortedArrays([1, 2], [3]) == 2 + assert Solution().findMedianSortedArrays([1], [2, 3]) == 2 + assert Solution().findMedianSortedArrays([1, 2], [1, 2]) == 1.5 diff --git a/003 Longest Substring Without Repeating Characters.py b/003 Longest Substring Without Repeating Characters.py index 3918520..c9c5747 100644 --- a/003 Longest Substring Without Repeating Characters.py +++ b/003 Longest Substring Without Repeating Characters.py @@ -4,6 +4,8 @@ is "b", with the length of 1. """ __author__ = 'Danyang' + + class Solution: def lengthOfLongestSubstring(self, s): """ @@ -21,7 +23,7 @@ def lengthOfLongestSubstring(self, s): start = 0 # pointer for ind, val in enumerate(s): - if visited_last_index[ord(val)]==-1: + if visited_last_index[ord(val)] == -1: longest = max(longest, (ind)-start+1) else: longest = max(longest, (ind-1)-start+1) diff --git a/004 Add Two Numbers.py b/004 Add Two Numbers.py index 7da571f..dcc6364 100644 --- a/004 Add Two Numbers.py +++ b/004 Add Two Numbers.py @@ -7,7 +7,8 @@ """ __author__ = 'Danyang' -# Definition for singly-linked list. + + class ListNode: def __init__(self, x): self.val = x @@ -17,6 +18,7 @@ def __repr__(self): # for debugging return repr(self.val) + class Solution: def addTwoNumbers(self, l1, l2): """ @@ -34,8 +36,8 @@ def addTwoNumbers(self, l1, l2): cur2 = l2 cur = result_head while cur1 or cur2: - cur.val = cur.val + self.addNode(cur1, cur2) - if cur.val<10: + cur.val = cur.val+self.addNode(cur1, cur2) + if cur.val < 10: if cur1 and cur1.next or cur2 and cur2.next: # next node cur.next = ListNode(0) else: @@ -64,9 +66,10 @@ def addNode(self, node1, node2): return node2.val if not node2: return node1.val - return node1.val + node2.val + return node1.val+node2.val + -if __name__=="__main__": +if __name__ == "__main__": l1s = [ListNode(1)] l2s = [ListNode(9), ListNode(9)] for i in range(len(l1s)-1): diff --git a/005 Longest Palindromic Substring.py b/005 Longest Palindromic Substring.py index d4abcfc..552bfb8 100644 --- a/005 Longest Palindromic Substring.py +++ b/005 Longest Palindromic Substring.py @@ -3,6 +3,8 @@ there exists one unique longest palindromic substring. """ __author__ = 'Danyang' + + class Solution: def longestPalindrome_TLE(self, s): """ @@ -33,13 +35,12 @@ def longestPalindrome_TLE(self, s): longest = [0, 0] for j in xrange(length+1): for i in xrange(j-1, -1, -1): - if i+1==j: + if i+1 == j: dp[i][j] = True else: - dp[i][j] = s[i]==s[j-1] and dp[i+1][j-1] # pre-access? starting backward - + dp[i][j] = s[i] == s[j-1] and dp[i+1][j-1] # pre-access? starting backward - if dp[i][j]==True and longest[1]-longest[0]len(longest): longest = current + if len(current) > len(longest): longest = current current = self.get_palindrome_from_center(s, i, i+1) - if len(current)>len(longest): longest = current + if len(current) > len(longest): longest = current return longest def get_palindrome_from_center(self, s, begin, end): """ # [begin, end] """ - while begin>=0 and end= 0 and end < len(s) and s[begin] == s[end]: begin -= 1 end += 1 return s[begin+1: end-1+1] -if __name__=="__main__": - assert Solution().longestPalindrome("dfaaabbbaaac")=="aaabbbaaa" \ No newline at end of file +if __name__ == "__main__": + assert Solution().longestPalindrome("dfaaabbbaaac") == "aaabbbaaa" \ No newline at end of file diff --git a/006 ZigZag Conversion.py b/006 ZigZag Conversion.py index af1d231..5a847f4 100644 --- a/006 ZigZag Conversion.py +++ b/006 ZigZag Conversion.py @@ -12,6 +12,8 @@ convert("PAYPALISHIRING", 3) should return "PAHNAPLSIIGYIR". """ __author__ = 'Danyang' + + class Solution: def convert(self, s, nRows): """ @@ -24,7 +26,7 @@ def convert(self, s, nRows): matrix = [[] for _ in xrange(nRows)] i = 0 - while iINT_MAX: + if result > INT_MAX: return INT_MAX - if result=10: + while x/div >= 10: div *= 10 # without touch x + while x > 0: + msb = x/div + lsb = x%10 - while x>0: - msb = x / div - lsb = x % 10 - - if msb!=lsb: + if msb != lsb: return False # shrink @@ -37,5 +38,5 @@ def isPalindrome(self, x): return True -if __name__=="__main__": +if __name__ == "__main__": Solution().isPalindrome(2147483647) \ No newline at end of file diff --git a/010 Container With Most Water.py b/010 Container With Most Water.py index 8540ae5..4ecd1aa 100644 --- a/010 Container With Most Water.py +++ b/010 Container With Most Water.py @@ -6,6 +6,8 @@ Note: You may not slant the container. """ __author__ = 'Danyang' + + class Solution: def maxArea(self, height): """ @@ -20,13 +22,13 @@ def maxArea(self, height): end = len(height)-1 max_area = -1<<32 - while start true """ __author__ = 'Danyang' + + class Solution: def isMatch_error(self, s, p): """ @@ -35,33 +37,33 @@ def isMatch_error(self, s, p): index = 0 state = 0 - while index=0 and regex[j-1]!="*": + if regex[j] == "*": + if j-1 >= 0 and regex[j-1] != "*": dp[i][j] = dp[i][j+1] # skip else: return False # two consecutive * - elif j+1=component: + while num >= component: string_builder.append(int2roman[component]) num -= component diff --git a/013 Roman to Integer.py b/013 Roman to Integer.py index 5f0f915..febf00f 100644 --- a/013 Roman to Integer.py +++ b/013 Roman to Integer.py @@ -13,6 +13,8 @@ "D": 500, "M": 1000 } + + class Solution: def romanToInt(self, s): """ @@ -23,7 +25,7 @@ def romanToInt(self, s): """ result = 0 for ind, val in enumerate(s): - if ind>0 and roman2int[val]>roman2int[s[ind-1]]: # e.g. XIV + if ind > 0 and roman2int[val] > roman2int[s[ind-1]]: # e.g. XIV result -= roman2int[s[ind-1]] # reverse last action result += roman2int[val]-roman2int[s[ind-1]] else: diff --git a/014 3Sum.py b/014 3Sum.py index 3888016..3100ae5 100644 --- a/014 3Sum.py +++ b/014 3Sum.py @@ -12,6 +12,8 @@ (-1, -1, 2) """ __author__ = 'Danyang' + + class Solution: def threeSum_duplicate(self, num): """ @@ -30,11 +32,11 @@ def threeSum_duplicate(self, num): result = [] for i in xrange(len(num)): for j in xrange(i, len(num)): - target = 0 - num[i] - num[j] + target = 0-num[i]-num[j] if target not in reverse_map: continue for index in reverse_map[target]: - if i!=index and j!=index: + if i != index and j != index: result.append([num[i], num[j], target]) break return result @@ -55,7 +57,6 @@ def threeSum_TLE(self, num): else: reverse_map[val].append(ind) - result = {} for i in xrange(len(num)): for j in xrange(i, len(num)): @@ -63,10 +64,10 @@ def threeSum_TLE(self, num): if target not in reverse_map: continue for index in reverse_map[target]: - if index!=i and index!=j: + if index != i and index != j: lst = sorted([num[i], num[j], target]) lst = tuple(lst) - result[lst] = 1 # hash + result[lst] = 1 # hash break return result.keys() @@ -97,36 +98,32 @@ def threeSum(self, num): result = [] num.sort() # sorting first, avoid duplicate, i = 0 - while i0: + elif sum(lst) > 0: k -= 1 else: j += 1 i += 1 # remove duplicate - while i Date: Wed, 23 Sep 2015 01:00:27 -0700 Subject: [PATCH 035/585] array --- 163 Missing Ranges.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 163 Missing Ranges.py diff --git a/163 Missing Ranges.py b/163 Missing Ranges.py new file mode 100644 index 0000000..3ad4a68 --- /dev/null +++ b/163 Missing Ranges.py @@ -0,0 +1,38 @@ +""" +Given a sorted integer array where the range of elements are [lower, upper] inclusive, return its missing ranges. + +For example, given [0, 1, 3, 50, 75], lower = 0 and upper = 99, return ["2", "4->49", "51->74", "76->99"]. +""" +__author__ = 'Daniel' + + +class Solution(object): + def findMissingRanges(self, nums, lower, upper): + """ + :type nums: List[int] + :type lower: int + :type upper: int + :rtype: List[str] + """ + n = len(nums) + ret = [] + if not nums: + ret.append([lower, upper]) + else: + if nums[0] > lower: + ret.append([lower, nums[0]-1]) + + for i in xrange(1, n): + if nums[i] > nums[i-1]+1: + ret.append([nums[i-1]+1, nums[i]-1]) + + if upper > nums[-1]: + ret.append([nums[-1]+1, upper]) + + def mapper(x): + if x[0] == x[1]: + return "%d" % x[0] + else: + return "%d->%d" % tuple(x) + + return map(mapper, ret) From 31620871dc642432eb380bead60b107adc3e2dfa Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 23 Sep 2015 11:59:22 -0700 Subject: [PATCH 036/585] str --- 161 One Edit Distance.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 161 One Edit Distance.py diff --git a/161 One Edit Distance.py b/161 One Edit Distance.py new file mode 100644 index 0000000..f90136a --- /dev/null +++ b/161 One Edit Distance.py @@ -0,0 +1,40 @@ +""" +Premium question +""" +__author__ = 'Daniel' + + +class Solution(object): + def isOneEditDistance(self, s, t): + """ + String + + :type s: str + :type t: str + :rtype: bool + """ + l_s = len(s) + l_t = len(t) + if abs(l_s-l_t) > 1: + return False + + if l_s > l_t: + s, t = t, s + l_s, l_t = l_t, l_s + + error = 0 + i, j = 0, 0 + while i < l_s and j < l_t: + if s[i] == t[j]: + i += 1 + j += 1 + else: + if l_s != l_t: + j += 1 + else: + i += 1 + j += 1 + + error += 1 + + return error == 1 or error == 0 and l_s != l_t From ae8d55ecbfa9815c97f1c77f0f4df63a7a97b414 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 23 Sep 2015 19:36:19 -0700 Subject: [PATCH 037/585] array --- 167 Two Sum II - Input array is sorted.py | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 167 Two Sum II - Input array is sorted.py diff --git a/167 Two Sum II - Input array is sorted.py b/167 Two Sum II - Input array is sorted.py new file mode 100644 index 0000000..154569f --- /dev/null +++ b/167 Two Sum II - Input array is sorted.py @@ -0,0 +1,26 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution(object): + def twoSum(self, numbers, target): + """ + :type numbers: List[int] + :type target: int + :rtype: List[int] + """ + n = len(numbers) + i = 0 + j = n-1 + while i < j: + s = numbers[i] + numbers[j] + if s == target: + return i+1, j+1 + elif s < target: + i += 1 + else: + j -= 1 + + return -1, -1 From 0e23570b118c40070053213839d93d9e8b114a8d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 23 Sep 2015 19:50:58 -0700 Subject: [PATCH 038/585] reverse words --- 151 Reverse Words in a String.py | 4 +++- 186 Reverse Words in a String II.py | 36 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 186 Reverse Words in a String II.py diff --git a/151 Reverse Words in a String.py b/151 Reverse Words in a String.py index 78f57bd..9a612aa 100644 --- a/151 Reverse Words in a String.py +++ b/151 Reverse Words in a String.py @@ -16,6 +16,8 @@ Reduce them to a single space in the reversed string. """ __author__ = 'Danyang' + + class Solution: def reverseWords(self, s): """ @@ -23,6 +25,6 @@ def reverseWords(self, s): :param s: a string :return: a string """ - words_lst = s.split() # not s.split(" ") + words_lst = s.split() # not s.split(" ") words_lst = reversed(words_lst) return ' '.join(words_lst) \ No newline at end of file diff --git a/186 Reverse Words in a String II.py b/186 Reverse Words in a String II.py new file mode 100644 index 0000000..5d43c16 --- /dev/null +++ b/186 Reverse Words in a String II.py @@ -0,0 +1,36 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution(object): + def reverseWords(self, s): + """ + in-place without allocating extra space + + :type s: a list of 1 length strings (List[str]) + :rtype: nothing + """ + self.reverse(s, 0, len(s)) + i = 0 + while i < len(s): + j = i+1 + while j < len(s) and s[j] != " ": + j += 1 + + self.reverse(s, i, j) + i = j+1 + + def reverse(self, s, start, end): + i = start + j = end + while i < j-1: + s[i], s[j-1] = s[j-1], s[i] + i += 1 + j -= 1 + +if __name__ == "__main__": + lst = list("the sky is blue") + Solution().reverseWords(lst) + assert "".join(lst) == "blue is sky the" \ No newline at end of file From 4c3baf57814e5bb9c44b0fff425202eddd00ee3a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 23 Sep 2015 20:11:24 -0700 Subject: [PATCH 039/585] hash map with python optimization --- 170 Two Sum III - Data structure design.py | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 170 Two Sum III - Data structure design.py diff --git a/170 Two Sum III - Data structure design.py b/170 Two Sum III - Data structure design.py new file mode 100644 index 0000000..c14c7a0 --- /dev/null +++ b/170 Two Sum III - Data structure design.py @@ -0,0 +1,45 @@ +""" +Premium Question +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class TwoSum(object): + def __init__(self): + """ + initialize your data structure here + """ + self.hash_map = defaultdict(int) + + def add(self, number): + """ + Add the number to an internal data structure. + :rtype: nothing + """ + self.hash_map[number] += 1 + + def find(self, value): + """ + Find if there exists any pair of numbers which sum is equal to the value. + :type value: int + :rtype: bool + """ + return any( + value-k in self.hash_map and (value-k != k or self.hash_map[k] > 1) + for k in self.hash_map + ) + + def find_TLE(self, value): + """ + Find if there exists any pair of numbers which sum is equal to the value. + :type value: int + :rtype: bool + """ + for k in self.hash_map.keys(): + target = value - k + if target in self.hash_map and (target != k or self.hash_map[target] > 1): + return True + + return False From a6c5a6e1f0074c8c0715d05070b44797e2c328a5 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 26 Sep 2015 23:53:02 -0700 Subject: [PATCH 040/585] buffer --- 157 Read N Characters Given Read4.py | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 157 Read N Characters Given Read4.py diff --git a/157 Read N Characters Given Read4.py b/157 Read N Characters Given Read4.py new file mode 100644 index 0000000..345fdb2 --- /dev/null +++ b/157 Read N Characters Given Read4.py @@ -0,0 +1,40 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +def read4(buf): + """ + read 4 chars to buf + + :type buf: List[str] + :rtype: int + """ + return 0 + + +class Solution(object): + def read(self, buf, n): + """ + read n chars to buf + :type buf: Destination buffer (List[str]) + :type n: Maximum number of characters to read (int) + :rtype: The number of characters read (int) + """ + idx = 0 + while idx < n: + buf4 = ["" for _ in xrange(4)] + r = read4(buf4) + if idx+r < n: + for i in xrange(r): + buf[idx+i] = buf4[i] + idx += r + if r < 4: + break + else: + for i in xrange(n-idx): + buf[idx+i] = buf4[i] + idx = n + + return idx \ No newline at end of file From 372e2f1691c2ec034caf8f011ee39c773c38eb79 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 28 Sep 2015 00:38:33 -0700 Subject: [PATCH 041/585] buffer --- ...rs Given Read4 II - Call multiple times.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 158 Read N Characters Given Read4 II - Call multiple times.py diff --git a/158 Read N Characters Given Read4 II - Call multiple times.py b/158 Read N Characters Given Read4 II - Call multiple times.py new file mode 100644 index 0000000..778414b --- /dev/null +++ b/158 Read N Characters Given Read4 II - Call multiple times.py @@ -0,0 +1,52 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +def read4(buf): + """ + read 4 chars to buf + + :type buf: List[str] + :rtype: int + """ + return 0 + + +class Solution(object): + def __init__(self): + self.prev = [] + + def read(self, buf, n): + """ + read n chars to buf, called multiple times + + :type buf: Destination buffer (List[str]) + :type n: Maximum number of characters to read (int) + :rtype: The number of characters read (int) + """ + l = min(len(self.prev), n) + for i in xrange(l): + buf[i] = self.prev[i] + self.prev = self.prev[l:] # pitfall self.prev = [] + + idx = l # the next reading + while idx < n: + buf4 = ["" for _ in xrange(4)] + r = read4(buf4) + if idx+r < n: + for i in xrange(idx, idx+r): + buf[i] = buf4[i-idx] + + idx += r + if r < 4: + return idx + else: + for i in xrange(idx, n): + buf[i] = buf4[i-idx] + + self.prev = buf4[n-idx:r] # pitfall buf4[n-idx:] + idx = n + + return idx \ No newline at end of file From 7365e4cf8144f0106845ba8359c7fcb8606ed2d3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 28 Sep 2015 16:02:15 -0700 Subject: [PATCH 042/585] sliding window --- ...ng with At Most Two Distinct Characters.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 159 Longest Substring with At Most Two Distinct Characters.py diff --git a/159 Longest Substring with At Most Two Distinct Characters.py b/159 Longest Substring with At Most Two Distinct Characters.py new file mode 100644 index 0000000..cff4bc5 --- /dev/null +++ b/159 Longest Substring with At Most Two Distinct Characters.py @@ -0,0 +1,35 @@ +""" +Premium Question +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class Solution(object): + def lengthOfLongestSubstringTwoDistinct(self, s): + """ + Sliding Window + :type s: str + :rtype: int + """ + m = defaultdict(int) + i = 0 + j = 0 + maxa = 0 + for j in xrange(len(s)): + m[s[j]] += 1 + while len(m) > 2: + m[s[i]] -= 1 + if m[s[i]] == 0: + del m[s[i]] + + i += 1 + + maxa = max(maxa, j-i+1) + + return maxa + + +if __name__ == "__main__": + print Solution().lengthOfLongestSubstringTwoDistinct("ecebaaaaaacdbb") \ No newline at end of file From 15c2cf133e0c3656adbbbd96ae06b02cda1449f9 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 28 Sep 2015 16:10:22 -0700 Subject: [PATCH 043/585] bst --- 285 Inorder Successor in BST.py | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 285 Inorder Successor in BST.py diff --git a/285 Inorder Successor in BST.py b/285 Inorder Successor in BST.py new file mode 100644 index 0000000..657f0bb --- /dev/null +++ b/285 Inorder Successor in BST.py @@ -0,0 +1,34 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def inorderSuccessor(self, root, p): + """ + search + :type root: TreeNode + :type p: TreeNode + :rtype: TreeNode + """ + find = [None] + self.search(root, p, find) + return find[0] + + def search(self, cur, p, find): + if not cur: + return + + if cur.val > p.val: + find[0] = cur + self.search(cur.left, p, find) + else: + self.search(cur.right, p, find) \ No newline at end of file From 1ade3aed25f3d40897f3d82bbfcd64ce8db138d7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 28 Sep 2015 19:32:49 -0700 Subject: [PATCH 044/585] bfs abstract bfs --- 286 Walls and Gates.py | 120 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 286 Walls and Gates.py diff --git a/286 Walls and Gates.py b/286 Walls and Gates.py new file mode 100644 index 0000000..04078df --- /dev/null +++ b/286 Walls and Gates.py @@ -0,0 +1,120 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution(object): + def __init__(self): + self.dirs = ((-1, 0), (1, 0), (0, -1), (0, 1)) + + def wallsAndGates(self, mat): + """ + bfs + O(mn), abstract level + + reference: https://leetcode.com/discuss/60170/6-lines-o-mn-python-bfs + :type mat: List[List[int]] + :rtype: void Do not return anything, modify rooms in-place instead. + """ + q = [(i, j) for i, row in enumerate(mat) for j, val in enumerate(row) if val == 0] + for i, j in q: # iterator + for d in self.dirs: + i1, j1 = i+d[0], j+d[1] + if 0 <= i1 < len(mat) and 0 <= j1 < len(mat[0]) and mat[i1][j1] > mat[i][j]+1: + mat[i1][j1] = mat[i][j]+1 + q.append((i1, j1)) + + +class Solution_slow(object): + def __init__(self): + self.dirs = ((-1, 0), (1, 0), (0, -1), (0, 1)) + + def wallsAndGates(self, rooms): + """ + bfs + O(kmn) where k is #0s + :type rooms: List[List[int]] + :rtype: void Do not return anything, modify rooms in-place instead. + """ + if not rooms: return + m = len(rooms) + if not m: return + n = len(rooms[0]) + + for i in xrange(m): + for j in xrange(n): + if rooms[i][j] == 0: + self.bfs_deque(rooms, i, j) + + def bfs(self, rooms, x, y): + m = len(rooms) + n = len(rooms[0]) + level = 0 + q = [(x, y)] + while q: + l = len(q) + for idx in xrange(l): + i, j = q[idx] + rooms[i][j] = min(rooms[i][j], level) + for d in self.dirs: + i_t = i+d[0] + j_t = j+d[1] + if 0 <= i_t < m and 0 <= j_t < n and rooms[i_t][j_t] != -1 and rooms[i_t][j_t] >= level+1: + q.append((i_t, j_t)) + + q = q[l:] + level += 1 + + def bfs_deque(self, rooms, x, y): + from collections import deque + + m = len(rooms) + n = len(rooms[0]) + q = deque() + q.append((x, y, 0)) + while q: + i, j, level = q.popleft() + rooms[i][j] = min(rooms[i][j], level) + for d in self.dirs: + i_t, j_t = i+d[0], j+d[1] + if 0 <= i_t < m and 0 <= j_t < n and rooms[i_t][j_t] != -1 and rooms[i_t][j_t] >= level+1: + q.append((i_t, j_t, level+1)) + + +class Solution_error(object): + def __init__(self): + self.dirs = ((-1, 0), (1, 0), (0, -1), (0, 1)) + + def wallsAndGates(self, rooms): + """ + post-order DFS + + :type rooms: List[List[int]] + :rtype: void Do not return anything, modify rooms in-place instead. + """ + if not rooms: return + m = len(rooms) + if not m: return + n = len(rooms[0]) + + visited = [[False for _ in xrange(n)] for _ in xrange(m)] + for i in xrange(m): + for j in xrange(n): + if not visited[i][j]: + self.dfs(rooms, i, j, visited) + + def dfs(self, rooms, i, j, visited): + if not visited[i][j]: + visited[i][j] = True + for d in self.dirs: + nxt_i = i+d[0] + nxt_j = j+d[1] + if rooms[nxt_i][nxt_j] != -1: + rooms[nxt_i][nxt_j] = min(rooms[nxt_i][nxt_j], self.dfs(rooms, nxt_i, nxt_j, visited)+1) + + return rooms[nxt_i][nxt_j] + + + + From 28b3e88c8a4822171213e26fff0d13b4d12b68e2 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 28 Sep 2015 20:37:34 -0700 Subject: [PATCH 045/585] Floyd's loop detection --- ...ng with At Most Two Distinct Characters.py | 2 +- 287 Find the Duplicate Number.py | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 287 Find the Duplicate Number.py diff --git a/159 Longest Substring with At Most Two Distinct Characters.py b/159 Longest Substring with At Most Two Distinct Characters.py index cff4bc5..0add713 100644 --- a/159 Longest Substring with At Most Two Distinct Characters.py +++ b/159 Longest Substring with At Most Two Distinct Characters.py @@ -32,4 +32,4 @@ def lengthOfLongestSubstringTwoDistinct(self, s): if __name__ == "__main__": - print Solution().lengthOfLongestSubstringTwoDistinct("ecebaaaaaacdbb") \ No newline at end of file + assert Solution().lengthOfLongestSubstringTwoDistinct("ecebaaaaaacdbb") == 7 \ No newline at end of file diff --git a/287 Find the Duplicate Number.py b/287 Find the Duplicate Number.py new file mode 100644 index 0000000..9668a94 --- /dev/null +++ b/287 Find the Duplicate Number.py @@ -0,0 +1,41 @@ +""" +Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one +duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one. + +Note: +You must not modify the array (assume the array is read only). +You must use only constant, O(1) extra space. +Your runtime complexity should be less than O(n^2). +There is only one duplicate number in the array, but it could be repeated more than once. +""" +__author__ = 'Daniel' + + +class Solution(object): + def findDuplicate(self, nums): + """ + Floyd's loop detection + Abstract the array to linked list, current index as value, current value as next + Need to derive the math proof + + Abstract array to linked list + :type nums: List[int] + :rtype: int + """ + f, s = 0, 0 + while True: + f = nums[nums[f]] + s = nums[s] + if f == s: + break + + t = 0 + while t != s: + t = nums[t] + s = nums[s] + + return t + +if __name__ == "__main__": + print Solution().findDuplicate([1, 2, 3 ,4, 5, 5]) + From aa35274af6c2d775560463bdeb065d34b7fdceb5 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 28 Sep 2015 21:59:54 -0700 Subject: [PATCH 046/585] update readme --- README.md | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 1d4b891..94e2487 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,14 @@ # LeetCode -LeetCode is a coding online judger. You may find the problem list [here](https://oj.leetcode.com/problems/). +LeetCode is a coding online judger. You may find the problem list here [LeetCode](https://leetcode.com/problemset/algorithms/). -###Battery Included -You can find comprehensive documentation in the source code itself. Documentation includes problem descriptions, algorithms, and test cases. - -``` - .-==============-. .-==============-. - | BATTERY | | INCLUDED | - (|+ -| (|+ -| - | | | | - '-===============' '-==============-' -``` ### Problem List -Indexed by chronological order: [Link](http://deepreader.io/LeetCode/problem_list.html). +Problem list indexed by chronological order: [Link](http://deepreader.io/LeetCode/problem_list.html). ### Feedback -Welcome to raise an issue [here](https://github.com/idf/LeetCode/issues) if you have any feedback. +Welcome to raise an issue [here](https://github.com/algorhythms/LeetCode/issues) if you have any feedback. ### Notes: TLE & MLE -Failed attempts are kept in the source code as documentation, which are annotated as TLE (Time Limit Exceeds) or MLE (Memory Limit Exceeds). +Failed attempts are kept in the source code as documentation, which are annotated as TLE (Time Limit Exceeded) or MLE (Memory Limit Exceeded). +### Disclaimer +The solutions in this repository are personal work, and in any form it neither represents any opinion of nor affiliates to LeetCode Corp. \ No newline at end of file From 359bf44ff22aae18b45b28057019480a201bc5ff Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 1 Oct 2015 22:38:58 -0700 Subject: [PATCH 047/585] hash --- 288 Unique Word Abbreviation.py | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 288 Unique Word Abbreviation.py diff --git a/288 Unique Word Abbreviation.py b/288 Unique Word Abbreviation.py new file mode 100644 index 0000000..f7637e8 --- /dev/null +++ b/288 Unique Word Abbreviation.py @@ -0,0 +1,35 @@ +""" +Premium Question +""" +from collections import defaultdict + + +__author__ = 'Daniel' + + +class ValidWordAbbr(object): + def __init__(self, dictionary): + """ + initialize your data structure here. + :type dictionary: List[str] + """ + self.abbrev = defaultdict(int) + self.dictionary = set(dictionary) + + for word in dictionary: + self.abbrev[self.process(word)] += 1 + + def process(self, word): + if len(word) <= 2: + return word + + return word[0]+str(len(word)-2)+word[-1] + + def isUnique(self, word): + """ + check if a word is unique. + :type word: str + :rtype: bool + """ + return (word in self.dictionary and self.abbrev[self.process(word)] == 1 or + not self.process(word) in self.abbrev) From 5424c5989fadb819a676664eefa65f203dc8eb77 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 6 Oct 2015 23:19:51 -0700 Subject: [PATCH 048/585] hash --- 290 Word Pattern.py | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 290 Word Pattern.py diff --git a/290 Word Pattern.py b/290 Word Pattern.py new file mode 100644 index 0000000..34c0b56 --- /dev/null +++ b/290 Word Pattern.py @@ -0,0 +1,58 @@ +""" +Given a pattern and a string str, find if str follows the same pattern. + +Examples: +pattern = "abba", str = "dog cat cat dog" should return true. +pattern = "abba", str = "dog cat cat fish" should return false. +pattern = "aaaa", str = "dog cat cat dog" should return false. +pattern = "abba", str = "dog dog dog dog" should return false. +Notes: +patterncontains only lowercase alphabetical letters, and str contains words separated by a single space. Each word in +str contains only lowercase alphabetical letters. +Both pattern and str do not have leading or trailing spaces. +Each letter in pattern must map to a word with length that is at least 1. +""" +__author__ = 'Daniel' + + +class OneToOneMap(object): + def __init__(self): + self.m = {} # keep a single map + + def set(self, a, b): + self.m[a] = b + self.m[b] = a + + def get(self, a): + return self.m.get(a) + + +class Solution(object): + def wordPattern(self, pattern, str): + """ + Simplify the condition in if-else + + :type pattern: str + :type str: str + :rtype: bool + """ + m = OneToOneMap() + lst = str.split(" ") + if len(pattern) != len(lst): + return False + + for i in xrange(len(pattern)): + a = m.get(pattern[i]) + b = m.get(lst[i]) + if a is None and b is None: + m.set(pattern[i], lst[i]) + elif a is None and b is not None: + return False + elif a != lst[i]: + return False + + return True + + +if __name__ == "__main__": + assert Solution().wordPattern("abba", "dog cat cat dog") == True From 12924fbf2465e81cbf5baa9be633b95a28745f71 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 7 Oct 2015 00:06:57 -0700 Subject: [PATCH 049/585] space optimization --- 289 Game of Life.py | 68 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 289 Game of Life.py diff --git a/289 Game of Life.py b/289 Game of Life.py new file mode 100644 index 0000000..6f81158 --- /dev/null +++ b/289 Game of Life.py @@ -0,0 +1,68 @@ +""" +According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a cellular automaton devised by +the British mathematician John Horton Conway in 1970." + +Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight +neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article): + +Any live cell with fewer than two live neighbors dies, as if caused by under-population. +Any live cell with two or three live neighbors lives on to the next generation. +Any live cell with more than three live neighbors dies, as if by over-population.. +Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction. +Write a function to compute the next state (after one update) of the board given its current state. + +Follow up: +Could you solve it in-place? Remember that the board needs to be updated at the same time: You cannot update some cells +first and then use their updated values to update other cells. +In this question, we represent the board using a 2D array. In principle, the board is infinite, which would cause +problems when the active area encroaches the border of the array. How would you address these problems? +""" + + +__author__ = 'Daniel' + + +class Solution(object): + def __init__(self): + self.dirs = [(-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1)] + + def gameOfLife(self, board): + """ + B3/S23 + + In-place solution + 1. Line buffer, directional, main the entires for previous state. + 2. higher bit, since you got 32-bit int + :type board: List[List[int]] + :rtype: void Do not return anything, modify board in-place instead. + """ + m = len(board) + n = len(board[0]) + lines = [[0 for _ in xrange(n)] for _ in xrange(2)] + for i in xrange(m): + for j in xrange(n): + lines[(i+1)%2][j] = board[i][j] + + cnt = 0 + for d in self.dirs: + I = i+d[0] + J = j+d[1] + if 0 <= I < m and 0 <= J < n: + if I < i: + cnt += lines[i%2][J] + elif I == i and J < j: + cnt += lines[(i+1)%2][J] + else: + cnt += board[I][J] + + if cnt == 3: + board[i][j] = 1 + elif cnt == 2: + board[i][j] &= 1 + else: + board[i][j] = 0 + + + + + From b949a171ccc32db0b78428dadc3c862c17a1705e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 7 Oct 2015 00:07:30 -0700 Subject: [PATCH 050/585] space optimization --- 289 Game of Life.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/289 Game of Life.py b/289 Game of Life.py index 6f81158..000cc8f 100644 --- a/289 Game of Life.py +++ b/289 Game of Life.py @@ -28,7 +28,7 @@ def __init__(self): def gameOfLife(self, board): """ - B3/S23 + B3/S23, born 3 stays 2 or 3 In-place solution 1. Line buffer, directional, main the entires for previous state. @@ -61,8 +61,3 @@ def gameOfLife(self, board): board[i][j] &= 1 else: board[i][j] = 0 - - - - - From f28ebf8bdf94acf7a23b0ae692ad506239d2c6b4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 7 Oct 2015 20:46:38 -0700 Subject: [PATCH 051/585] doc --- 202 Happy Number.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/202 Happy Number.py b/202 Happy Number.py index b4002de..c64d94e 100644 --- a/202 Happy Number.py +++ b/202 Happy Number.py @@ -20,6 +20,9 @@ class Solution: def isHappy(self, n): """ Start with several simple cases and find the pattern. + + if converge to 1, return True + if loop, return False :param n: :rtype: bool """ From 6bc4d62959cbb7b8f46a54bc5490cbb4ae8e68a8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 7 Oct 2015 22:57:03 -0700 Subject: [PATCH 052/585] dfs with arithmetic --- 282 Expression Add Operators.py | 50 +++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 282 Expression Add Operators.py diff --git a/282 Expression Add Operators.py b/282 Expression Add Operators.py new file mode 100644 index 0000000..50710c4 --- /dev/null +++ b/282 Expression Add Operators.py @@ -0,0 +1,50 @@ +""" +Given a string that contains only digits 0-9 and a target value, return all possibilities to add binary operators (not +unary) +, -, or * between the digits so they evaluate to the target value. + +Examples: +"123", 6 -> ["1+2+3", "1*2*3"] +"232", 8 -> ["2*3+2", "2+3*2"] +"105", 5 -> ["1*0+5","10-5"] +"00", 0 -> ["0+0", "0-0", "0*0"] +"3456237490", 9191 -> [] +""" +__author__ = 'Daniel' + + +class Solution(object): + def addOperators(self, num, target): + """ + Adapted from https://leetcode.com/discuss/58614/java-standard-backtrace-ac-solutoin-short-and-clear + + Algorithm: + 1. DFS + 2. Special handling for multiplication + :type num: str + :type target: int + :rtype: List[str] + """ + ret = [] + self.dfs(num, target, 0, "", 0, 0, ret) + return ret + + def dfs(self, num, target, pos, cur_str, cur_val, mul, ret): + if pos >= len(num): + if cur_val == target: + ret.append(cur_str) + else: + for i in xrange(pos, len(num)): + if i != pos and num[pos] == '0': + continue + nxt_val = int(num[pos:i+1]) + + if not cur_str: + self.dfs(num, target, i+1, "%d"%nxt_val, nxt_val, nxt_val, ret) + else: + self.dfs(num, target, i+1, cur_str+"+%d"%nxt_val, cur_val+nxt_val, nxt_val, ret) + self.dfs(num, target, i+1, cur_str+"-%d"%nxt_val, cur_val-nxt_val, -nxt_val, ret) + self.dfs(num, target, i+1, cur_str+"*%d"%nxt_val, cur_val-mul+mul*nxt_val, mul*nxt_val, ret) + + +if __name__ == "__main__": + assert Solution().addOperators("232", 8) == ['2+3*2', '2*3+2'] From 860fbe0f7f5026718eaf30e7202143b3bed0bafb Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 8 Oct 2015 00:48:25 -0700 Subject: [PATCH 053/585] to run faster, cache the depth results --- 110 Balanced Binary Tree.py | 47 +++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/110 Balanced Binary Tree.py b/110 Balanced Binary Tree.py index af1eb57..b99bb3f 100644 --- a/110 Balanced Binary Tree.py +++ b/110 Balanced Binary Tree.py @@ -5,14 +5,49 @@ every node never differ by more than 1. """ __author__ = 'Danyang' -# Definition for a binary tree node -class TreeNode: + + +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: + +class Solution(object): + def __init__(self): + self.depth_bottom = {} + + def isBalanced(self, root): + self.fathom(root, 0) + return self._is_balanced(root, 0) + + def _is_balanced(self, cur, depth): + """ + :param depth: depth from root to current node. + """ + if not cur: + return True + + h1 = h2 = depth + if cur.left: h1 = self.depth_bottom[cur.left] + if cur.right: h2 = self.depth_bottom[cur.right] + + if abs(h1 - h2) > 1: + return False + + return all([self._is_balanced(cur.left, depth+1), self._is_balanced(cur.right, depth+1)]) + + def fathom(self, root, depth): + if not root: + return depth-1 + + ret = max(self.fathom(root.left, depth+1), self.fathom(root.right, depth+1)) + self.depth_bottom[root] = ret + return ret + + +class Solution_slow(object): def isBalanced(self, root): """ pre-order traversal @@ -22,7 +57,7 @@ def isBalanced(self, root): """ if not root: return True - if abs(self.fathom(root.left, 0)-self.fathom(root.right, 0))>1: + if abs(self.fathom(root.left, 0)-self.fathom(root.right, 0)) > 1: return False if self.isBalanced(root.left) and self.isBalanced(root.right): @@ -30,12 +65,10 @@ def isBalanced(self, root): else: return False - - def fathom(self, root, depth): """ DFS """ if not root: return depth-1 # test cases - return max(self.fathom(root.left, depth + 1), self.fathom(root.right, depth + 1)) + return max(self.fathom(root.left, depth+1), self.fathom(root.right, depth+1)) From 3a8b1bf8795e899d9b49f59230bf2f51d6247dc5 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 8 Oct 2015 10:59:53 -0700 Subject: [PATCH 054/585] pivot --- 283 Move Zeroes.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/283 Move Zeroes.py b/283 Move Zeroes.py index a9c9943..fe948ac 100644 --- a/283 Move Zeroes.py +++ b/283 Move Zeroes.py @@ -15,6 +15,19 @@ class Solution(object): def moveZeroes(self, nums): """ + Pivot + """ + left = -1 + for i in xrange(len(nums)): + if nums[i] != 0: + left += 1 + nums[left], nums[i] = nums[i], nums[left] + + +class SolutionCount(object): + def moveZeroes(self, nums): + """ + In-place :type nums: List[int] :rtype: void Do not return anything, modify nums in-place instead. """ From e43acf8b0501fb7f9d0632285013cd85ec03b706 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 9 Oct 2015 00:44:56 -0700 Subject: [PATCH 055/585] doc --- 110 Balanced Binary Tree.py | 2 +- 261 Graph Valid Tree.py | 14 ++++++------ 263 Ugly Number.py | 2 +- 264 Ugly Number II.py | 1 + 265 Paint House II.py | 13 +++++++---- 266 Palindrome Permutation.py | 1 + 267 Palindrome Permutation II.py | 25 +++++++++------------- 269 Alien Dictionary.py | 12 +++++------ 270 Closest Binary Search Tree Value.py | 3 +++ 271 Encode and Decode Strings.py | 4 ++-- 272 Closest Binary Search Tree Value II.py | 3 +++ 274 H-Index.py | 2 +- 275 H-Index II.py | 1 + 276 Paint Fence.py | 4 +++- 279 Perfect Squares.py | 1 + 282 Expression Add Operators.py | 5 +++-- 285 Inorder Successor in BST.py | 2 ++ 287 Find the Duplicate Number.py | 4 +++- 289 Game of Life.py | 1 + 19 files changed, 59 insertions(+), 41 deletions(-) diff --git a/110 Balanced Binary Tree.py b/110 Balanced Binary Tree.py index b99bb3f..d365791 100644 --- a/110 Balanced Binary Tree.py +++ b/110 Balanced Binary Tree.py @@ -47,7 +47,7 @@ def fathom(self, root, depth): return ret -class Solution_slow(object): +class SolutionSlow(object): def isBalanced(self, root): """ pre-order traversal diff --git a/261 Graph Valid Tree.py b/261 Graph Valid Tree.py index 7d12a02..59e2fa5 100644 --- a/261 Graph Valid Tree.py +++ b/261 Graph Valid Tree.py @@ -22,22 +22,22 @@ def validTree(self, n, edges): V[edge[1]].append(edge[0]) visited = set() - marked = set() - if not self.dfs(V, edges[0][0], None, visited, marked): + path_set = set() + if not self.dfs(V, edges[0][0], None, visited, path_set): return False return len(visited) == n - def dfs(self, V, k, pi, visited, marked): - if k in marked: + def dfs(self, V, k, pi, visited, path_set): + if k in path_set: return False - marked.add(k) + path_set.add(k) for neighbor in V[k]: if neighbor != pi: - if not self.dfs(V, neighbor, k, visited, marked): + if not self.dfs(V, neighbor, k, visited, path_set): return False - marked.remove(k) + path_set.remove(k) visited.add(k) return True \ No newline at end of file diff --git a/263 Ugly Number.py b/263 Ugly Number.py index d449b25..d117de8 100644 --- a/263 Ugly Number.py +++ b/263 Ugly Number.py @@ -12,7 +12,7 @@ class Solution(object): def isUgly(self, num): """ - Prime factor + Prime factors: 2, 3, 5 :type num: int :rtype: bool diff --git a/264 Ugly Number II.py b/264 Ugly Number II.py index 2d76783..ea37cb8 100644 --- a/264 Ugly Number II.py +++ b/264 Ugly Number II.py @@ -27,6 +27,7 @@ def __cmp__(self, other): class Solution(object): def nthUglyNumber(self, n): """ + Prime factor: 2, 3, 5 Heap :type n: int :rtype: int diff --git a/265 Paint House II.py b/265 Paint House II.py index 2b3f062..dcbc35f 100644 --- a/265 Paint House II.py +++ b/265 Paint House II.py @@ -10,6 +10,9 @@ class Solution(object): def minCostII(self, costs): """ Lef F[i][j] be the total min costs when the houses BEFORE i are painted, with (i-1)-th house pained as color j + F[i][j] = \min(F[i-1][k] + cost[i-1][j] \forall k, k != j + + edge case handling for i :type costs: List[List[int]] :rtype: int """ @@ -21,10 +24,12 @@ def minCostII(self, costs): F = [[0 for _ in xrange(m)] for _ in xrange(n+1)] for i in xrange(1, n+1): for k1 in xrange(m): - F[i][k1] = sys.maxint - for k0 in xrange(m): - if i == 1 or k1 != k0: - F[i][k1] = min(F[i][k1], F[i-1][k0]+costs[i-1][k1]) + F[i][k1] = min( + F[i-1][k0]+costs[i-1][k1] + # if i == 1 or k1 != k0 else sys.maxint # another syntax + for k0 in xrange(m) + if i == 1 or k1 != k0 + ) return min(F[n][i] for i in xrange(m)) diff --git a/266 Palindrome Permutation.py b/266 Palindrome Permutation.py index 63092cb..18f615f 100644 --- a/266 Palindrome Permutation.py +++ b/266 Palindrome Permutation.py @@ -1,5 +1,6 @@ """ Premium Question +https://leetcode.com/problems/palindrome-permutation/ """ from collections import defaultdict diff --git a/267 Palindrome Permutation II.py b/267 Palindrome Permutation II.py index eab8ab5..a1eb026 100644 --- a/267 Palindrome Permutation II.py +++ b/267 Palindrome Permutation II.py @@ -1,11 +1,6 @@ """ -Given a string s, return all the palindromic permutations (without duplicates) of it. Return an empty list if no palindromic permutation could be form. - -For example: - -Given s = "aabb", return ["abba", "baab"]. - -Given s = "abc", return []. +Premium Question +https://leetcode.com/problems/palindrome-permutation-ii/ """ from collections import defaultdict @@ -40,18 +35,18 @@ def generatePalindromes(self, s): self.grow(s, m, None, cur, ret) return ret - def grow(self, s, m, pi, cur, ret): + def grow(self, s, count_map, pi, cur, ret): if len(cur) == len(s): ret.append(cur) return - for k in m.keys(): - if k != pi and m[k] > 0: - for i in xrange(1, m[k]/2+1): - m[k] -= i*2 - self.grow(s, m, k, k*i+cur+k*i, ret) - m[k] += i*2 + for k in count_map.keys(): + if k != pi and count_map[k] > 0: + for i in xrange(1, count_map[k]/2+1): # jump the parent + count_map[k] -= i*2 + self.grow(s, count_map, k, k*i+cur+k*i, ret) + count_map[k] += i*2 if __name__ == "__main__": - print Solution().generatePalindromes("aabb") \ No newline at end of file + assert Solution().generatePalindromes("aabb") == ['baab', 'abba'] \ No newline at end of file diff --git a/269 Alien Dictionary.py b/269 Alien Dictionary.py index b9ef473..15517bd 100644 --- a/269 Alien Dictionary.py +++ b/269 Alien Dictionary.py @@ -50,25 +50,25 @@ def construct_graph(self, words, up, down, ptr, V): i = j - def topological_dfs(self, V, cur, visited, marked, ret): + def topological_dfs(self, V, cur, visited, pathset, ret): """ :param V: Vertices HashMap :param cur: currently visiting letter :param visited: visited letters - :param marked: marked predecessor in the path + :param pathset: marked predecessor in the path :param ret: the path, ordered topologically :return: whether contains cycles """ - if cur in marked: + if cur in pathset: return False - marked.add(cur) + pathset.add(cur) for nei in V[cur]: if nei not in visited: - if not self.topological_dfs(V, nei, visited, marked, ret): + if not self.topological_dfs(V, nei, visited, pathset, ret): return False - marked.remove(cur) + pathset.remove(cur) visited.add(cur) ret.append(cur) return True diff --git a/270 Closest Binary Search Tree Value.py b/270 Closest Binary Search Tree Value.py index 26fcdea..b4b1c9a 100644 --- a/270 Closest Binary Search Tree Value.py +++ b/270 Closest Binary Search Tree Value.py @@ -16,6 +16,9 @@ def __init__(self, x): class Solution(object): def closestValue(self, root, target): """ + Divide the problem into 2 parts: + 1. find the value just smaller than target + 2. find the value just larger than target :type root: TreeNode :type target: float :rtype: int diff --git a/271 Encode and Decode Strings.py b/271 Encode and Decode Strings.py index 8e01c1f..c7069ad 100644 --- a/271 Encode and Decode Strings.py +++ b/271 Encode and Decode Strings.py @@ -35,7 +35,7 @@ def decode(self, s): return strs -class Codec(object): +class CodecMethod2(object): def encode(self, strs): """ Encodes a list of strings to a single string. @@ -60,7 +60,7 @@ def decode(self, s): return map(lambda x: x.replace("\n\n", "\n"), strs) -class Codec_error(object): +class CodecError(object): def encode(self, strs): """ Encodes a list of strings to a single string. diff --git a/272 Closest Binary Search Tree Value II.py b/272 Closest Binary Search Tree Value II.py index be7013a..060f967 100644 --- a/272 Closest Binary Search Tree Value II.py +++ b/272 Closest Binary Search Tree Value II.py @@ -1,5 +1,6 @@ """ Premium Question +https://leetcode.com/problems/closest-binary-search-tree-value-ii/ """ __author__ = 'Daniel' @@ -33,6 +34,7 @@ def closestKValues(self, root, target, k): def predecessors(self, root, target, stk): if not root: return + self.predecessors(root.left, target, stk) if root.val <= target: stk.append(root.val) @@ -41,6 +43,7 @@ def predecessors(self, root, target, stk): def successors(self, root, target, stk): if not root: return + self.successors(root.right, target, stk) if root.val > target: stk.append(root.val) diff --git a/274 H-Index.py b/274 H-Index.py index cdd0aff..c868470 100644 --- a/274 H-Index.py +++ b/274 H-Index.py @@ -20,7 +20,7 @@ def hIndex(self, citations): Reverse mapping & DP Determine the range of h-index Chunk by n - Let F[i] be the number of paper with >= i citations + Let F[i] be the #paper with i citations (later transform F[i] to #paoer with >= i citations :type citations: List[int] :rtype: int """ diff --git a/275 H-Index II.py b/275 H-Index II.py index 28b6b8c..2d44f12 100644 --- a/275 H-Index II.py +++ b/275 H-Index II.py @@ -7,6 +7,7 @@ class Solution(object): def hIndex(self, citations): """ + Given sorted -> binary search From linear search into bin search :type citations: List[int] :rtype: int diff --git a/276 Paint Fence.py b/276 Paint Fence.py index 32f27cb..790587d 100644 --- a/276 Paint Fence.py +++ b/276 Paint Fence.py @@ -10,6 +10,8 @@ def numWays_oneliner(self, n, k): def numWays(self, n, k): """ + You need to abstract number of colors to binary value (is different color) + Let F1[i] be the number of ways for A[:i] with last two with different colors F2[i] be the number of ways for A[:i] with last two with same color @@ -109,6 +111,6 @@ def numWays_MLE(self, n, k): return ret if __name__ == "__main__": - print Solution().numWays(3, 2) + assert Solution().numWays(3, 2) == 6 diff --git a/279 Perfect Squares.py b/279 Perfect Squares.py index 29bf7df..a4bb1fc 100644 --- a/279 Perfect Squares.py +++ b/279 Perfect Squares.py @@ -13,6 +13,7 @@ class Solution(object): def numSquares(self, n): """ bfs + the q stores the intermediate result of sum of squares :type n: int :rtype: int """ diff --git a/282 Expression Add Operators.py b/282 Expression Add Operators.py index 50710c4..391c444 100644 --- a/282 Expression Add Operators.py +++ b/282 Expression Add Operators.py @@ -20,6 +20,7 @@ def addOperators(self, num, target): Algorithm: 1. DFS 2. Special handling for multiplication + 3. Detect invalid number with leading 0's :type num: str :type target: int :rtype: List[str] @@ -34,7 +35,7 @@ def dfs(self, num, target, pos, cur_str, cur_val, mul, ret): ret.append(cur_str) else: for i in xrange(pos, len(num)): - if i != pos and num[pos] == '0': + if i != pos and num[pos] == "0": continue nxt_val = int(num[pos:i+1]) @@ -47,4 +48,4 @@ def dfs(self, num, target, pos, cur_str, cur_val, mul, ret): if __name__ == "__main__": - assert Solution().addOperators("232", 8) == ['2+3*2', '2*3+2'] + assert Solution().addOperators("232", 8) == ["2+3*2", "2*3+2"] diff --git a/285 Inorder Successor in BST.py b/285 Inorder Successor in BST.py index 657f0bb..c788eed 100644 --- a/285 Inorder Successor in BST.py +++ b/285 Inorder Successor in BST.py @@ -15,6 +15,8 @@ class Solution(object): def inorderSuccessor(self, root, p): """ search + + If it is a general binary tree :type root: TreeNode :type p: TreeNode :rtype: TreeNode diff --git a/287 Find the Duplicate Number.py b/287 Find the Duplicate Number.py index 9668a94..07daaed 100644 --- a/287 Find the Duplicate Number.py +++ b/287 Find the Duplicate Number.py @@ -14,7 +14,9 @@ class Solution(object): def findDuplicate(self, nums): """ - Floyd's loop detection + Degenerated case: if there is only one duplicates, just do arithmetic. + + For possibly multiple duplications: Floyd's loop detection Abstract the array to linked list, current index as value, current value as next Need to derive the math proof diff --git a/289 Game of Life.py b/289 Game of Life.py index 000cc8f..7304e37 100644 --- a/289 Game of Life.py +++ b/289 Game of Life.py @@ -31,6 +31,7 @@ def gameOfLife(self, board): B3/S23, born 3 stays 2 or 3 In-place solution + Similar to dp space optimization. 1. Line buffer, directional, main the entires for previous state. 2. higher bit, since you got 32-bit int :type board: List[List[int]] From 8e706adde30b5b4d1d582e29b2b7615e7f4bad7f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 11 Oct 2015 23:23:26 -0700 Subject: [PATCH 056/585] backtracking --- 291 Word Pattern II.py | 49 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 291 Word Pattern II.py diff --git a/291 Word Pattern II.py b/291 Word Pattern II.py new file mode 100644 index 0000000..f9992e6 --- /dev/null +++ b/291 Word Pattern II.py @@ -0,0 +1,49 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution(object): + def wordPatternMatch(self, pattern, s): + """ + Backtracking with prune + :type pattern: str + :type s: str + :rtype: bool + """ + return self.dfs(pattern, s, {}, set()) + + def dfs(self, pattern, s, char2word, words): + """ + Loop & DFS + :return: pattern can match s + """ + if not pattern and s or not s and pattern: + return False + + if not pattern and not s: + return True + + + if pattern[0] in char2word: + word = char2word[pattern[0]] + if s[:len(word)] != word: + return False + else: + assert word in words + return self.dfs(pattern[1:], s[len(word):], char2word, words) + else: + for i in xrange(len(s)): + word = s[:i+1] + if word in words: + continue + + char2word[pattern[0]] = word + words.add(word) + if self.dfs(pattern[1:], s[len(word):], char2word, words): + return True + words.remove(word) + del char2word[pattern[0]] + + return False \ No newline at end of file From 2715622a64850be529791570a5a237a3029648eb Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 11 Oct 2015 23:28:37 -0700 Subject: [PATCH 057/585] fix solution, use dict + set --- 290 Word Pattern.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/290 Word Pattern.py b/290 Word Pattern.py index 34c0b56..7862c62 100644 --- a/290 Word Pattern.py +++ b/290 Word Pattern.py @@ -15,6 +15,29 @@ __author__ = 'Daniel' +class Solution(object): + def wordPattern(self, pattern, s): + lst = s.split(" ") + if len(pattern) != len(lst): + return False + + char2word = {} + words = set() + for i in xrange(len(pattern)): + if pattern[i] in char2word: + if char2word[pattern[i]] != lst[i]: + return False + else: + assert lst[i] in words + else: + if lst[i] in words: + return False + char2word[pattern[i]] = lst[i] + words.add(lst[i]) + + return True + + class OneToOneMap(object): def __init__(self): self.m = {} # keep a single map @@ -27,10 +50,10 @@ def get(self, a): return self.m.get(a) -class Solution(object): +class SolutionError(object): def wordPattern(self, pattern, str): """ - Simplify the condition in if-else + May not always work due to OneToOneMap implementation in the case that a word is 1-letter. :type pattern: str :type str: str From 3ec868676cd10d65ba5271032cf30ac922c9be5f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 12 Oct 2015 12:01:16 -0700 Subject: [PATCH 058/585] game theory --- 292 Nim Game.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 292 Nim Game.py diff --git a/292 Nim Game.py b/292 Nim Game.py new file mode 100644 index 0000000..477d48c --- /dev/null +++ b/292 Nim Game.py @@ -0,0 +1,59 @@ +""" +You are playing the following Nim Game with your friend: There is a heap of stones on the table, each time one of you +take turns to remove 1 to 3 stones. The one who removes the last stone will be the winner. You will take the first turn +to remove the stones. + +Both of you are very clever and have optimal strategies for the game. Write a function to determine whether you can win +the game given the number of stones in the heap. + +For example, if there are 4 stones in the heap, then you will never win the game: no matter 1, 2, or 3 stones you remove +, the last stone will always be removed by your friend. +""" +__author__ = 'Daniel' + + +class Solution(object): + def canWinNim(self, n): + """ + Enumerate example and find the pattern + """ + return n % 4 != 0 + + def canWinNim_TLE(self, n): + """ + dp + + Let F[i] be the whether act & win when there is i stones left + :type n: int + :rtype: bool + """ + if n < 3: + return True + + F = [False for _ in xrange(3)] + F[1] = F[2] = F[0] = True + for i in xrange(4, n+1): + F[i%3] = any(not F[(i-k)%3] for k in xrange(1, 4)) + + return F[n%3] + + def canWinNim_MLE(self, n): + """ + dp + + Let F[i] be the whether act & win when there is i stones left + :type n: int + :rtype: bool + """ + if n < 3: + return True + + F = [False for _ in xrange(n+1)] + F[1] = F[2] = F[3] = True + for i in xrange(4, n+1): + F[i] = any(not F[i-k] for k in xrange(1, 4)) + + return F[n] + +if __name__ == "__main__": + assert Solution().canWinNim(5) == True \ No newline at end of file From 2bdce39b2edac564aadc4ee035f560b4ecc5e266 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 13 Oct 2015 16:36:10 -0700 Subject: [PATCH 059/585] update --- 157 Read N Characters Given Read4.py | 8 +++++--- 292 Nim Game.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/157 Read N Characters Given Read4.py b/157 Read N Characters Given Read4.py index 345fdb2..9f8f312 100644 --- a/157 Read N Characters Given Read4.py +++ b/157 Read N Characters Given Read4.py @@ -18,6 +18,8 @@ class Solution(object): def read(self, buf, n): """ read n chars to buf + Algorithm: + Two ptrs :type buf: Destination buffer (List[str]) :type n: Maximum number of characters to read (int) :rtype: The number of characters read (int) @@ -33,8 +35,8 @@ def read(self, buf, n): if r < 4: break else: - for i in xrange(n-idx): - buf[idx+i] = buf4[i] + for i in xrange(idx, n): + buf[i] = buf4[i-idx] idx = n - return idx \ No newline at end of file + return idx diff --git a/292 Nim Game.py b/292 Nim Game.py index 477d48c..8443b68 100644 --- a/292 Nim Game.py +++ b/292 Nim Game.py @@ -56,4 +56,4 @@ def canWinNim_MLE(self, n): return F[n] if __name__ == "__main__": - assert Solution().canWinNim(5) == True \ No newline at end of file + assert Solution().canWinNim(5) \ No newline at end of file From de7800a513435066796b84e0cfea74a212d44b0a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 14 Oct 2015 23:02:21 -0700 Subject: [PATCH 060/585] Straightforward --- 293 Flip Game.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 293 Flip Game.py diff --git a/293 Flip Game.py b/293 Flip Game.py new file mode 100644 index 0000000..909b908 --- /dev/null +++ b/293 Flip Game.py @@ -0,0 +1,19 @@ +""" +Premium Question +Straightforward +""" +__author__ = 'Daniel' + + +class Solution(object): + def generatePossibleNextMoves(self, s): + """ + :type s: str + :rtype: List[str] + """ + ret = [] + for i in xrange(len(s)-1): + if s[i:i+2] == "++": + ret.append(s[:i]+"--"+s[i+2:]) + + return ret \ No newline at end of file From 9cfeb13cf11ea9802d2ed602e3c919c26f03a32b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 15 Oct 2015 20:16:59 -0700 Subject: [PATCH 061/585] refactor --- 259 3Sum Smaller.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/259 3Sum Smaller.py b/259 3Sum Smaller.py index b238fcf..eb95c8e 100644 --- a/259 3Sum Smaller.py +++ b/259 3Sum Smaller.py @@ -1,10 +1,11 @@ """ Premium Question +Smaller than the target. """ __author__ = 'Daniel' -class Solution: +class Solution(object): def threeSumSmaller(self, nums, target): """ @@ -16,13 +17,13 @@ def threeSumSmaller(self, nums, target): cnt = 0 n = len(nums) for i in xrange(n-2): - j = i+1 - k = n-1 - while j < k: - if nums[i]+nums[j]+nums[k] < target: - cnt += k-j - j += 1 + l = i+1 + h = n-1 + while l < h: + if nums[i]+nums[l]+nums[h] < target: + cnt += h-l # move the high ptr leftward till low. + l += 1 else: - k -= 1 + h -= 1 return cnt \ No newline at end of file From ba7c370dd2c58caac7f170a938f855608a9e33da Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 15 Oct 2015 20:17:45 -0700 Subject: [PATCH 062/585] backtracking --- 294 Flip Game II.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 294 Flip Game II.py diff --git a/294 Flip Game II.py b/294 Flip Game II.py new file mode 100644 index 0000000..6195c91 --- /dev/null +++ b/294 Flip Game II.py @@ -0,0 +1,26 @@ +""" +Premium Question +Game, Winner, Backtracking +""" +__author__ = 'Daniel' + + +class Solution(object): + def canWin(self, s): + return any(not self.canWin(s[:i]+"--"+s[i+2:]) for i in xrange(len(s)-1) if s[i:i+2] == "++") + + def canWin_trivial(self, s): + """ + :type s: str + :rtype: bool + """ + for i in xrange(len(s)-1): + if s[i:i+2] == "++": + if not self.canWin(s[:i]+"--"+s[i+2:]): + return True + + return False + + +if __name__ == "__main__": + assert Solution().canWin("+++++") == False \ No newline at end of file From e839b36fb3cf611f58ed36d7d5ee455fbd858e44 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 15 Oct 2015 20:54:40 -0700 Subject: [PATCH 063/585] update --- 065 Plus One.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/065 Plus One.py b/065 Plus One.py index f118678..fd24e57 100644 --- a/065 Plus One.py +++ b/065 Plus One.py @@ -4,7 +4,9 @@ The digits are stored such that the most significant digit is at the head of the list. """ __author__ = 'Danyang' -class Solution: + + +class Solution(object): def plusOne(self, digits): """ Math @@ -13,20 +15,15 @@ def plusOne(self, digits): :param digits: a list of integer digits :return: a list of integer digits """ - cur = len(digits)-1 - while True: - if cur>=0: - digits[cur] += 1 - if digits[cur]<10: - break - else: - digits[cur] -= 10 - cur -= 1 + for i in xrange(len(digits)-1, -1, -1): + digits[i] += 1 + if digits[i] < 10: + return digits else: - # MSB - digits.insert(0, 1) - break + digits[i] -= 10 + # MSB + digits.insert(0, 1) return digits def plusOne(self, digits): @@ -41,7 +38,7 @@ def plusOne(self, digits): carry = 0 for i in xrange(len(digits)): # for ind, val in enumerate(digits): digits[i] += carry - if digits[i]>9: + if digits[i] > 9: digits[i] -= 10 carry = 1 else: @@ -54,6 +51,7 @@ def plusOne(self, digits): digits.reverse() return digits -if __name__=="__main__": + +if __name__ == "__main__": digits = [9] - assert Solution().plusOne(digits)==[1, 0] \ No newline at end of file + assert Solution().plusOne(digits) == [1, 0] \ No newline at end of file From 97ed66e5a4e1ac65fabcb615b69d9f495509ec4d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 15 Oct 2015 21:21:08 -0700 Subject: [PATCH 064/585] update --- 253 Meeting Rooms II.py | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/253 Meeting Rooms II.py b/253 Meeting Rooms II.py index 719c534..51724fc 100644 --- a/253 Meeting Rooms II.py +++ b/253 Meeting Rooms II.py @@ -1,6 +1,11 @@ """ Premium Question +Find the maximum number of overlapped intervals """ +import heapq +import operator + + __author__ = 'Daniel' @@ -10,10 +15,7 @@ def __init__(self, s=0, e=0): self.end = e -import heapq - - -class Solution: +class Solution(object): def minMeetingRooms(self, intervals): """ @@ -22,21 +24,13 @@ def minMeetingRooms(self, intervals): """ maxa = 0 - intervals.sort(cmp=Solution.cmp) - end_heap = [] + intervals.sort(key=operator.attrgetter("start")) + h_end = [] for itvl in intervals: - heapq.heappush(end_heap, itvl.end) - while end_heap and end_heap[0] <= itvl.start: - heapq.heappop(end_heap) - - maxa = max(maxa, len(end_heap)) - - return maxa - + heapq.heappush(h_end, itvl.end) + while h_end and h_end[0] <= itvl.start: + heapq.heappop(h_end) - @staticmethod - def cmp(a, b): - if a.start != b.start: - return a.start-b.start + maxa = max(maxa, len(h_end)) - return a.end-b.end + return maxa \ No newline at end of file From 658494d28f92db218a53de05ffa17dee11be0c47 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 15 Oct 2015 21:30:43 -0700 Subject: [PATCH 065/585] update --- 163 Missing Ranges.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/163 Missing Ranges.py b/163 Missing Ranges.py index 3ad4a68..4506ecf 100644 --- a/163 Missing Ranges.py +++ b/163 Missing Ranges.py @@ -18,21 +18,22 @@ def findMissingRanges(self, nums, lower, upper): ret = [] if not nums: ret.append([lower, upper]) - else: - if nums[0] > lower: - ret.append([lower, nums[0]-1]) + return map(self.mapper, ret) + + if nums[0] > lower: + ret.append([lower, nums[0]-1]) - for i in xrange(1, n): - if nums[i] > nums[i-1]+1: - ret.append([nums[i-1]+1, nums[i]-1]) + for i in xrange(1, n): + if nums[i] > nums[i-1]+1: + ret.append([nums[i-1]+1, nums[i]-1]) - if upper > nums[-1]: - ret.append([nums[-1]+1, upper]) + if upper > nums[-1]: + ret.append([nums[-1]+1, upper]) - def mapper(x): - if x[0] == x[1]: - return "%d" % x[0] - else: - return "%d->%d" % tuple(x) + return map(self.mapper, ret) - return map(mapper, ret) + def mapper(self, x): + if x[0] == x[1]: + return "%d" % x[0] + else: + return "%d->%d" % tuple(x) From a769a7f546d2eeaba9563bad8c6042f4d8e278f7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 18 Oct 2015 13:18:54 -0700 Subject: [PATCH 066/585] two states dp --- 152 Maximum Product Subarray.py | 63 ++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/152 Maximum Product Subarray.py b/152 Maximum Product Subarray.py index 8a3dfb0..01ed007 100644 --- a/152 Maximum Product Subarray.py +++ b/152 Maximum Product Subarray.py @@ -5,7 +5,63 @@ the contiguous subarray [2,3] has the largest product = 6 """ __author__ = 'Danyang' -class Solution: + + +class Solution(object): + def maxProduct_oneline(self, nums): + return max(reduce(lambda A, n: [max(A), min(n, A[1]*n, A[2]*n), max(n, A[1]*n, A[2]*n)], nums[1:], [nums[0]]*3)) + + def maxProduct(self, nums): + """ + DP + State definitions: + let small[i] be the smallest product result ending with i + let large[i] be the largest product result ending with i + Transition functions: + small[i] = min(A[i], small[i-1]*A[i], large[i-1]*A[i] + large[i] = max(A[i], small[i-1]*A[i], large[i-1]*A[i] + + DP space can be optimized + :type nums: List[int] + :rtype: int + """ + small = nums[0] + large = nums[0] + maxa = nums[0] + for a in nums[1:]: + small, large = min(a, small*a, large*a), max(a, small*a, large*a) + maxa = max(maxa, small, large) + + return maxa + + def maxProduct_error2(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + if len(nums) < 2: + return max(nums) + + n = len(nums) + F_pos = [0 for _ in xrange(n+1)] + F_neg = [0 for _ in xrange(n+1)] + + maxa = 1 + for i in xrange(1, n+1): + v = nums[i-1] + if v > 0: + F_pos[i] = F_pos[i-1]*v if F_pos[i-1] != 0 else v + F_neg[i] = F_neg[i-1]*v + elif v == 0: + F_pos[i], F_neg[i] = 0, 0 + else: + F_neg[i] = min(0, F_pos[i-1]*v) + F_pos[i] = max(0, F_neg[i-1]*v) + + maxa = max(maxa, F_pos[i]) + + return maxa + def maxProduct_error(self, A): """ dp, collect number of negative number @@ -51,7 +107,7 @@ def maxProduct_error(self, A): return global_max - def maxProduct(self, A): + def maxProduct_dp(self, A): """ dp, collect number of negative number (notice 0). negative number and 0 will be special in this question @@ -103,12 +159,11 @@ def maxProduct(self, A): global_max = max(global_max, cur) - - return global_max if __name__=="__main__": + print Solution().maxProduct([2,3,-2,4]) assert Solution().maxProduct([2,-5,-2,-4,3])==24 assert Solution().maxProduct([-2, 0, -1])==0 assert Solution().maxProduct([-2])==-2 From dd96df240d945fa642bdac63d2790af5fa4841c3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 20 Oct 2015 11:33:26 -0700 Subject: [PATCH 067/585] Strobogrammatic --- 246 Strobogrammatic Number.py | 11 ++++++- 247 Strobogrammatic Number II.py | 40 ++++++++++++++++++++++- 248 Strobogrammatic Number III.py | 53 +++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 248 Strobogrammatic Number III.py diff --git a/246 Strobogrammatic Number.py b/246 Strobogrammatic Number.py index 81befed..11119d2 100644 --- a/246 Strobogrammatic Number.py +++ b/246 Strobogrammatic Number.py @@ -1,5 +1,7 @@ """ Premium Question +Checking Strobogrammatic +https://leetcode.com/problems/strobogrammatic-number/ """ __author__ = 'Daniel' @@ -15,13 +17,20 @@ def __init__(self): } def isStrobogrammatic(self, num): + for i in xrange(len(num)/2+1): + if num[i] not in self.map or self.map[num[i]] != num[len(num)-1-i]: + return False + + return True + + def isStrobogrammatic_tedious(self, num): """ :type num: str :rtype: bool """ num = list(num) - rev = [] + rev = [] # reverse for digit in reversed(num): try: rev.append(self.map[digit]) diff --git a/247 Strobogrammatic Number II.py b/247 Strobogrammatic Number II.py index 7178779..0070e82 100644 --- a/247 Strobogrammatic Number II.py +++ b/247 Strobogrammatic Number II.py @@ -1,10 +1,45 @@ """ Premium Question +Generation +https://leetcode.com/problems/strobogrammatic-number-ii/ """ +from collections import deque __author__ = 'Daniel' class Solution(object): + def __init__(self): + self.lst = ["11", "69", "88", "96", "00"] # use list rather than map since no need to look up + self.middle = ["0", "1", "8"] + + def findStrobogrammatic(self, n): + ret = [] + self.build(n, deque(), ret) + return ret + + def build(self, n, cur, ret): + """ + build from inside + """ + if n%2 == 1 and len(cur) == 0: + for elt in self.middle: + cur.append(elt) + self.build(n, cur, ret) + cur.pop() + else: + if len(cur) == n: + ret.append("".join(cur)) + return + for elt in self.lst: + if not (elt == "00" and len(cur) == n-2): + cur.appendleft(elt[0]) + cur.append(elt[1]) + self.build(n, cur, ret) + cur.pop() + cur.popleft() + + +class SolutionArray(object): def __init__(self): self.map1 = ["11", "69", "88", "96", "00"] @@ -18,6 +53,9 @@ def findStrobogrammatic(self, n): return ret def build(self, n, cur, ret): + """ + Using list as double-entry queue, performance of every operation is O(n) rather than O(1) + """ if n%2 == 1 and len(cur) == 0: for i in ["0", "1", "8"]: cur.append(i) @@ -83,4 +121,4 @@ def build(self, idx, n, cur, ret): cur.pop() if __name__ == "__main__": - print Solution().findStrobogrammatic(1) \ No newline at end of file + assert Solution().findStrobogrammatic(3) == ['101', '609', '808', '906', '111', '619', '818', '916', '181', '689', '888', '986'] \ No newline at end of file diff --git a/248 Strobogrammatic Number III.py b/248 Strobogrammatic Number III.py new file mode 100644 index 0000000..3d5a4ca --- /dev/null +++ b/248 Strobogrammatic Number III.py @@ -0,0 +1,53 @@ +""" +Premium Question +Generation +https://leetcode.com/problems/strobogrammatic-number-iii/ +""" +from collections import deque + +__author__ = 'Daniel' + + +class Solution(object): + def __init__(self): + self.lst = ["11", "69", "88", "96", "00"] + self.middle = ["0", "1", "8"] + + def strobogrammaticInRange(self, low, high): + """ + :type low: str + :type high: str + :rtype: int + """ + cnt = 0 + for l in xrange(len(low), len(high)+1): + cnt += len(filter(lambda x: int(low) <= int(x) <= int(high), self.strobogrammatic(l))) + + return cnt + + # below methods from strobogrammatic number ii + def strobogrammatic(self, n): + ret = [] + self.build(n, deque(), ret) + return ret + + def build(self, n, cur, ret): + """ + build from inside + """ + if n%2 == 1 and len(cur) == 0: + for elt in self.middle: + cur.append(elt) + self.build(n, cur, ret) + cur.pop() + else: + if len(cur) == n: + ret.append("".join(cur)) + return + for elt in self.lst: + if not (elt == "00" and len(cur) == n-2): + cur.appendleft(elt[0]) + cur.append(elt[1]) + self.build(n, cur, ret) + cur.pop() + cur.popleft() From 7eafdc35eb6dc6310618eb96c0df438b3dfb57f0 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 20 Oct 2015 11:42:42 -0700 Subject: [PATCH 068/585] dual heap --- 295 Find Median from Data Stream.py | 87 +++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 295 Find Median from Data Stream.py diff --git a/295 Find Median from Data Stream.py b/295 Find Median from Data Stream.py new file mode 100644 index 0000000..0412106 --- /dev/null +++ b/295 Find Median from Data Stream.py @@ -0,0 +1,87 @@ +""" +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 + +Design a data structure that supports the following two operations: + +void addNum(int num) - Add a integer number from the data stream to the data structure. +double findMedian() - Return the median of all elements so far. +For example: + +add(1) +add(2) +findMedian() -> 1.5 +add(3) +findMedian() -> 2 +""" +import heapq + +__author__ = 'Daniel' + + +class DualHeap(object): + def __init__(self): + """ + Dual Heap is great in the case where there is no removal. + :return: + """ + self.min_h = [] + self.max_h = [] + + def insert(self, num): + if not self.min_h or num > self.min_h[0]: + heapq.heappush(self.min_h, num) + else: + heapq.heappush(self.max_h, -num) + self.balance() + + def balance(self): + l1 = len(self.min_h) + l2 = len(self.max_h) + if l1-l2 > 1: + heapq.heappush(self.max_h, -heapq.heappop(self.min_h)) + self.balance() + elif l2-l1 > 1: + 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: + return -self.max_h[0] + else: + return self.min_h[0] + else: + return (-self.max_h[0]+self.min_h[0])/2.0 + + +class MedianFinder(object): + def __init__(self): + """ + Initialize your data structure here. + """ + self.dh = DualHeap() + + def addNum(self, num): + """ + Adds a num into the data structure. + :type num: int + :rtype: void + """ + self.dh.insert(num) + + def findMedian(self): + """ + Returns the median of current data stream + :rtype: float + """ + return self.dh.get_median() From 43cd3ee51834ffb0e0a47b4d69ed6bdd19056887 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 20 Oct 2015 14:41:10 -0700 Subject: [PATCH 069/585] update house robber --- 198 House Robber.py | 9 ++++++++- 213 House Robber II.py | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/198 House Robber.py b/198 House Robber.py index 5623401..f0a7cd9 100644 --- a/198 House Robber.py +++ b/198 House Robber.py @@ -14,7 +14,14 @@ def rob(self, nums): """ DP O(n) - f_i = max(f_{i-1}, f_{i-2} + A[i]) + Let F_i be max value END AT or BEFORE i + F_i = max(F_{i-1}, F_{i-2} + A[i]) + + Notes: + If change the definition of F_i + Let F_i be mex value END AT i + F_i = max(F_{i-2-k}+A[i] for k \in [0, i-2]), + Then time complexity is quadratic """ n = len(nums) f = [0 for _ in xrange(n+2)] diff --git a/213 House Robber II.py b/213 House Robber II.py index 33dc1e3..e58dcfb 100644 --- a/213 House Robber II.py +++ b/213 House Robber II.py @@ -15,6 +15,7 @@ class Solution: def rob(self, nums): """ + Two cases: cannot touch 1st element vs. cannot touch 2nd element. There are two cases here 1) 1st element is included and last is not included 2) 1st is not included and last is included. :type nums: list From 214124ef66d924f9c123d1eaea3bc2c954ccbdd7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 21 Oct 2015 13:29:58 -0700 Subject: [PATCH 070/585] palindrome --- 132 Palindrome Partitioning II.py | 176 ++++++++++++++++++------------ 1 file changed, 106 insertions(+), 70 deletions(-) diff --git a/132 Palindrome Partitioning II.py b/132 Palindrome Partitioning II.py index a1f7c2c..3e5c2b2 100644 --- a/132 Palindrome Partitioning II.py +++ b/132 Palindrome Partitioning II.py @@ -7,7 +7,106 @@ Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut. """ __author__ = 'Danyang' -class Solution: + + +class Solution(object): + def minCut(self, s): + """ + Let P[i][j] indicates whether s[i:j] is palindrome + P[i][j] = P[i+1][j-1] && s[i] == s[j-1] + + Left C[i] represents the min cut for s[:i] + C[i] = 0 if s[:i] is palindrome + C[i] = min(C[j]+1 for j Date: Wed, 21 Oct 2015 20:04:22 -0700 Subject: [PATCH 071/585] mahanttan distance --- 296 Best Meeting Point.py | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 296 Best Meeting Point.py diff --git a/296 Best Meeting Point.py b/296 Best Meeting Point.py new file mode 100644 index 0000000..b7560b8 --- /dev/null +++ b/296 Best Meeting Point.py @@ -0,0 +1,44 @@ +""" +Premium Question +Manhattan Distance +""" +__author__ = 'Daniel' + + +class Solution(object): + def minTotalDistance(self, grid): + x = sorted([i for i, row in enumerate(grid) for v in row if v == 1]) + y = sorted([j for row in grid for j, v in enumerate(row) if v == 1]) + return sum([abs(x[len(x)/2]-i)+abs(y[len(y)/2]-j) for i, row in enumerate(grid) for j, v in enumerate(row) if v == 1]) + + def minTotalDistance(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + x = [] + y = [] + + m = len(grid) + n = len(grid[0]) + for i in xrange(m): + for j in xrange(n): + if grid[i][j] == 1: + x.append(i) + y.append(j) + + x.sort() + y.sort() + cnt = len(x) + point = (x[cnt/2], y[cnt/2]) + ret = 0 + for i in xrange(m): + for j in xrange(n): + if grid[i][j] == 1: + ret += abs(point[0]-i) + ret += abs(point[1]-j) + + return ret + +if __name__ == "__main__": + assert Solution().minTotalDistance([[1,0,0,0,1],[0,0,0,0,0],[0,0,1,0,0]]) == 6 \ No newline at end of file From 8983ba9dc664785713fec3cbd53991a9c4483af2 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 21 Oct 2015 20:04:45 -0700 Subject: [PATCH 072/585] mahanttan distance --- 296 Best Meeting Point.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/296 Best Meeting Point.py b/296 Best Meeting Point.py index b7560b8..c749745 100644 --- a/296 Best Meeting Point.py +++ b/296 Best Meeting Point.py @@ -6,7 +6,7 @@ class Solution(object): - def minTotalDistance(self, grid): + def minTotalDistance_3lines(self, grid): x = sorted([i for i, row in enumerate(grid) for v in row if v == 1]) y = sorted([j for row in grid for j, v in enumerate(row) if v == 1]) return sum([abs(x[len(x)/2]-i)+abs(y[len(y)/2]-j) for i, row in enumerate(grid) for j, v in enumerate(row) if v == 1]) From b7f556b03a5c09fad23c919224f439d847d7a199 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 22 Oct 2015 23:55:34 -0700 Subject: [PATCH 073/585] memoization --- 294 Flip Game II.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/294 Flip Game II.py b/294 Flip Game II.py index 6195c91..17c7e11 100644 --- a/294 Flip Game II.py +++ b/294 Flip Game II.py @@ -6,17 +6,37 @@ class Solution(object): + def __init__(self): + self.d = {} + def canWin(self, s): - return any(not self.canWin(s[:i]+"--"+s[i+2:]) for i in xrange(len(s)-1) if s[i:i+2] == "++") + """ + memoization + 110ms + """ + if s not in self.d: + flag = False + for i in xrange(len(s)-1): + if s[i:i+2] == "++": + if not self.canWin(s[:i]+"--"+s[i+2:]): + flag = True + break + self.d[s] = flag + + return self.d[s] + + def canWin_oneline(self, s): + return any(not self.canWin_oneline(s[:i]+"--"+s[i+2:]) for i in xrange(len(s)-1) if s[i:i+2] == "++") def canWin_trivial(self, s): """ + 3200 ms :type s: str :rtype: bool """ for i in xrange(len(s)-1): if s[i:i+2] == "++": - if not self.canWin(s[:i]+"--"+s[i+2:]): + if not self.canWin_trivial(s[:i]+"--"+s[i+2:]): return True return False From 4eee849a0f9f0324cda0e40279e9d70a091dc845 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 23 Oct 2015 21:54:03 -0700 Subject: [PATCH 074/585] update reversing linked list --- 206 Reverse Linked List.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/206 Reverse Linked List.py b/206 Reverse Linked List.py index 6a7f777..59096c2 100644 --- a/206 Reverse Linked List.py +++ b/206 Reverse Linked List.py @@ -4,13 +4,13 @@ __author__ = 'Daniel' -class ListNode: +class ListNode(object): def __init__(self, x): self.val = x self.next = None -class Solution: +class Solution(object): def reverseList(self, head): """ :type head: ListNode @@ -26,6 +26,8 @@ def reverseList(self, head): cur = pre.next while pre and cur: pre, cur.next, cur = cur, pre, cur.next + # incorrect evaluation order + # pre, cur, cur.next = cur, cur.next, pre dummy.next.next = None # original head return pre # new head From e4e424a3fb71734d2193b4db00cf623ecdd0edd5 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 26 Oct 2015 20:46:59 -0700 Subject: [PATCH 075/585] add condition for convert the array problem to linked list problem --- 287 Find the Duplicate Number.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/287 Find the Duplicate Number.py b/287 Find the Duplicate Number.py index 07daaed..4d9e1a6 100644 --- a/287 Find the Duplicate Number.py +++ b/287 Find the Duplicate Number.py @@ -14,6 +14,9 @@ class Solution(object): def findDuplicate(self, nums): """ + Condition for convert the array problem to linked list problem: + CANNOT contains integer with 0; otherwise cycle: A = [1, 0] + Degenerated case: if there is only one duplicates, just do arithmetic. For possibly multiple duplications: Floyd's loop detection @@ -39,5 +42,5 @@ def findDuplicate(self, nums): return t if __name__ == "__main__": - print Solution().findDuplicate([1, 2, 3 ,4, 5, 5]) + assert Solution().findDuplicate([1, 2, 3 ,4, 5, 5]) == 5 From 31ab58207c0d33d8a4f70b9fb09283a89c8919fb Mon Sep 17 00:00:00 2001 From: "Daniel D. Zhang" Date: Mon, 26 Oct 2015 22:44:54 -0700 Subject: [PATCH 076/585] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 94e2487..4986aff 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# LeetCode +# LeetCode ![Language](https://img.shields.io/badge/language-Python-blue.svg) ![License](https://img.shields.io/badge/license-MIT-red.svg) LeetCode is a coding online judger. You may find the problem list here [LeetCode](https://leetcode.com/problemset/algorithms/). ### Problem List @@ -11,4 +11,4 @@ Welcome to raise an issue [here](https://github.com/algorhythms/LeetCode/issues) Failed attempts are kept in the source code as documentation, which are annotated as TLE (Time Limit Exceeded) or MLE (Memory Limit Exceeded). ### Disclaimer -The solutions in this repository are personal work, and in any form it neither represents any opinion of nor affiliates to LeetCode Corp. \ No newline at end of file +The solutions in this repository are personal work, and in any form it neither represents any opinion of nor affiliates to LeetCode Corp. From 1a73a412f8ee89afff0300c6f1019e645efb613d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 28 Oct 2015 23:34:18 -0700 Subject: [PATCH 077/585] bfs --- 297 Serialize and Deserialize Binary Tree.py | 100 +++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 297 Serialize and Deserialize Binary Tree.py diff --git a/297 Serialize and Deserialize Binary Tree.py b/297 Serialize and Deserialize Binary Tree.py new file mode 100644 index 0000000..9c589e0 --- /dev/null +++ b/297 Serialize and Deserialize Binary Tree.py @@ -0,0 +1,100 @@ +""" +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 tree. There is no restriction on how your serialization/ +deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this +string can be deserialized to the original tree structure. + +For example, you may serialize the following tree + + 1 + / \ + 2 3 + / \ + 4 5 +as "[1,2,3,null,null,4,5]", just the same as how LeetCode OJ serializes a binary tree. You do not necessarily need to +follow this format, so please be creative and come up with different approaches yourself. +Note: Do not use class member/global/static variables to store states. Your serialize and deserialize algorithms should +be stateless. +""" +from collections import deque + +__author__ = 'Daniel' + + +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Codec: + def serialize(self, root): + """ + bfs + Encodes a tree to a single string. + + encode to: 1, 2, 3, null, null, 4, 5, null, null, null, null + :type root: TreeNode + :rtype: str + """ + if not root: + return "null" + + ret = [] + q = [] + q.append(root) + ret.append(str(root.val)) # add result when enqueue + while q: + l = len(q) + for i in xrange(l): + cur = q[i] + if cur.left: + q.append(cur.left) + ret.append(str(cur.left.val)) + else: + ret.append("null") + + if cur.right: + q.append(cur.right) + ret.append(str(cur.right.val)) + else: + ret.append("null") + + q = q[l:] + + return ",".join(ret) + + def deserialize(self, data): + """ + Decodes your encoded data to tree. + decode: 1, 2, 3, null, null, 4, 5, null, null, null, null + :type data: str + :rtype: TreeNode + """ + lst = data.split(",") + root = self.decode(lst[0]) + q = deque([root]) + i = 1 + while i < len(lst): + cur = q.popleft() + cur.left = self.decode(lst[i]) + i += 1 + if cur.left: + q.append(cur.left) + if i < len(lst): + cur.right = self.decode(lst[i]) + i += 1 + if cur.right: + q.append(cur.right) + + return root + + def decode(self, s): + if s == "null": + return None + else: + return TreeNode(int(s)) From 0e8d9319c77e890a026833bc066b7e13fbe3682f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 29 Oct 2015 11:19:20 -0700 Subject: [PATCH 078/585] add cache --- 096 Unique Binary Search Trees II.py | 50 +++++++++++++++++++++------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/096 Unique Binary Search Trees II.py b/096 Unique Binary Search Trees II.py index 565d963..94941fa 100644 --- a/096 Unique Binary Search Trees II.py +++ b/096 Unique Binary Search Trees II.py @@ -12,30 +12,59 @@ confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ. """ __author__ = 'Danyang' + + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: +class Solution(object): + def __init__(self): + self.cache = {} + def generateTrees(self, n): """ dfs - Catalan: https://www.youtube.com/watch?v=QdcujZTp_8M (Forth proof) + Catalan :param n: integer :return: list of TreeNode """ - if n==0: + if n == 0: return [None] - return self.generate(1, n) + return self.generate_cache(1, n) + + def generate_cache(self, start, end): + """80ms""" + if (start, end) not in self.cache: + roots = [] + if start > end: + roots.append(None) + return roots + + for pivot in range(start, end+1): + left_roots = self.generate_cache(start, pivot-1) + right_roots = self.generate_cache(pivot+1, end) + for left_root in left_roots: + for right_root in right_roots: + root = TreeNode(pivot) + root.left = left_root + root.right = right_root + + roots.append(root) + + self.cache[(start, end)] = roots + + return self.cache[(start, end)] def generate(self, start, end): """ - dfs without dp + dfs (cache possible) + 100 ms {number| number \in [start, end]} Follow the 1st proof of Catalan Number @@ -46,15 +75,15 @@ def generate(self, start, end): subtree_roots = [] # trivial - if start>end: + if start > end: subtree_roots.append(None) return subtree_roots # pivot # list of unique subtrees = list of unique left subtrees, pivot, list of unique right subtrees for pivot in range(start, end+1): - left_subtree_roots = self.generate(start, pivot-1) # no dp yet - right_subtree_roots = self.generate(pivot+1, end) # no dp yet + left_subtree_roots = self.generate(start, pivot-1) + right_subtree_roots = self.generate(pivot+1, end) for left_node in left_subtree_roots: for right_node in right_subtree_roots: @@ -64,7 +93,4 @@ def generate(self, start, end): subtree_roots.append(pivot_node) - return subtree_roots - - From 1246b1ddeabb1a4b2ecd8c761023a21e008ff4af Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 31 Oct 2015 00:38:36 -0700 Subject: [PATCH 079/585] doc --- 150 Evaluate Reverse Polish Notation.py | 27 ++++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/150 Evaluate Reverse Polish Notation.py b/150 Evaluate Reverse Polish Notation.py index 4213e02..8f4f78c 100644 --- a/150 Evaluate Reverse Polish Notation.py +++ b/150 Evaluate Reverse Polish Notation.py @@ -1,5 +1,7 @@ __author__ = 'Danyang' -class Solution: + + +class Solution(object): def evalRPN(self, tokens): """ stack @@ -9,22 +11,23 @@ def evalRPN(self, tokens): :return: """ ops = ["+", "-", "*", "/"] + def arith(a, b, op): - if (op=="+"): - return a + b - if (op=="-"): - return a - b - if (op=="/"): + if (op == "+"): + return a+b + if (op == "-"): + return a-b + if (op == "/"): # return a/b # python treat differently for division 6/-132 is -1 - return int(float(a) / b) # round towards 0 - if (op=="*"): - return a * b + return int(float(a)/b) # round towards 0 + if (op == "*"): + return a*b # function is first-order class # not supported by leetcode # import operator # ops = { - # "+": operator.add, + # "+": operator.add, # "-": operator.sub, # "*": operator.mul, # "/": operator.div, @@ -46,5 +49,5 @@ def arith(a, b, op): return stack.pop() -if __name__=="__main__": - Solution().evalRPN(["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]) \ No newline at end of file +if __name__ == "__main__": + assert Solution().evalRPN(["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]) == 22 \ No newline at end of file From 41b6972d06995b5179fd15fb0883d8b927877f3f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 31 Oct 2015 11:46:46 -0700 Subject: [PATCH 080/585] fix dp --- 122 Best Time to Buy and Sell Stock.py | 30 +++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/122 Best Time to Buy and Sell Stock.py b/122 Best Time to Buy and Sell Stock.py index 7c842ce..a67f147 100644 --- a/122 Best Time to Buy and Sell Stock.py +++ b/122 Best Time to Buy and Sell Stock.py @@ -7,7 +7,30 @@ __author__ = 'Danyang' -class Solution: +class Solution(object): + def maxProfit(self, prices): + """ + Maximum subarray sum + DP version + Let F[i] be the maximum subarray sum ending at A[i-1] + """ + if len(prices) <= 1: + return 0 + delta_prices = [] + for i in xrange(1, len(prices)): + delta_prices.append(prices[i]-prices[i-1]) + + + A = delta_prices + n = len(A) + F = [0 for _ in xrange(n+1)] + maxa = A[0] + for i in xrange(1, n+1): + F[i] = max(F[i-1]+A[i-1], 0) + maxa = max(maxa, F[i]) + + return maxa + def maxProfit(self, prices): """ Only long position allowed, cannot short @@ -31,10 +54,7 @@ def maxProfit(self, prices): max_sub_array = 0 current_sub_array = 0 for j in xrange(len(delta_prices)): - if current_sub_array+delta_prices[j] >= 0: - current_sub_array += delta_prices[j] - else: - current_sub_array = 0 + current_sub_array = max(0, current_sub_array+delta_prices[j]) max_sub_array = max(max_sub_array, current_sub_array) return max_sub_array From a343089d7b3c044196a1233f9aa595720a610410 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 2 Nov 2015 15:47:59 -0800 Subject: [PATCH 081/585] static dp --- 279 Perfect Squares.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/279 Perfect Squares.py b/279 Perfect Squares.py index a4bb1fc..1ffbca9 100644 --- a/279 Perfect Squares.py +++ b/279 Perfect Squares.py @@ -5,12 +5,32 @@ For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9. """ import math +import sys __author__ = 'Daniel' class Solution(object): + F = [0] # static dp for all test cases def numSquares(self, n): + """ + static dp + F_i = min(F_{i - j^2}+1, \forall j) + + O(n), think it as a tree, cache tree O(m+n) = O(2n); rather than O(n sqrt(n)) + backward + """ + while len(Solution.F) <= n: + i = len(Solution.F) + Solution.F.append(sys.maxint) + j = 1 + while i - j*j >= 0: + Solution.F[i] = min(Solution.F[i], Solution.F[i-j*j]+1) + j += 1 + + return Solution.F[n] + + def numSquares_bfs(self, n): """ bfs the q stores the intermediate result of sum of squares From 6ac61b47ce09dfc1b2ccf05b2437cb6487cc9ae8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 2 Nov 2015 16:13:31 -0800 Subject: [PATCH 082/585] counter --- 299 Bulls and Cows.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 299 Bulls and Cows.py diff --git a/299 Bulls and Cows.py b/299 Bulls and Cows.py new file mode 100644 index 0000000..24177a3 --- /dev/null +++ b/299 Bulls and Cows.py @@ -0,0 +1,36 @@ +from collections import defaultdict + +__author__ = 'Daniel' + + +class Solution(object): + def getHint(self, secret, guess): + """ + :type secret: str + :type guess: str + :rtype: str + """ + hm = defaultdict(int) + A = 0 + B = 0 + for c in secret: + hm[c] += 1 + + for i, v in enumerate(guess): + if v == secret[i]: + A += 1 + hm[v] -= 1 + if hm[v] < 0: + assert hm[v] == -1 + B -= 1 + hm[v] = 0 + + elif v in hm and hm[v] > 0: + B += 1 + hm[v] -= 1 + + return "%dA%dB" % (A, B) + + +if __name__ == "__main__": + print Solution().getHint("0", "1") From 37d86b1cdfef8129a309d4220537766797f8162c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 3 Nov 2015 10:03:27 -0800 Subject: [PATCH 083/585] morris --- 095 Binary Tree Inorder Traversal.py | 33 ++++++++++++++++++++++++--- 144 Binary Tree Preorder Traversal.py | 30 ++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/095 Binary Tree Inorder Traversal.py b/095 Binary Tree Inorder Traversal.py index 753f078..e929ac0 100644 --- a/095 Binary Tree Inorder Traversal.py +++ b/095 Binary Tree Inorder Traversal.py @@ -15,15 +15,42 @@ confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ. """ __author__ = 'Danyang' -# Definition for a binary tree node -class TreeNode: + + +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: + +class Solution(object): def inorderTraversal(self, root): + """ + Morris Traversal + """ + ret = [] + cur = root + while cur: + if not cur.left: + ret.append(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 + ret.append(cur.val) + cur = cur.right + + return ret + + def inorderTraversal_memory(self, root): """ :type root: TreeNode :param root: diff --git a/144 Binary Tree Preorder Traversal.py b/144 Binary Tree Preorder Traversal.py index e4c345d..792f991 100644 --- a/144 Binary Tree Preorder Traversal.py +++ b/144 Binary Tree Preorder Traversal.py @@ -13,15 +13,41 @@ Note: Recursive solution is trivial, could you do it iteratively? - see preTraverse_itr """ __author__ = 'Danyang' + + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: + +class Solution(object): def preorderTraversal(self, root): + """Morris""" + ret = [] + cur = root + while cur: + if not cur.left: + ret.append(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 + ret.append(cur.val) + cur = cur.left + else: + pre.right = None + cur = cur.right + + return ret + + def preorderTraversal_memory(self, root): """ dfs :param root: From 2c206dc1e5a77c3fb8b9e541727703910d47ca0b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 3 Nov 2015 23:41:08 -0800 Subject: [PATCH 084/585] LIS --- 300 Longest Increasing Subsequence.py | 83 +++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 300 Longest Increasing Subsequence.py diff --git a/300 Longest Increasing Subsequence.py b/300 Longest Increasing Subsequence.py new file mode 100644 index 0000000..ab57891 --- /dev/null +++ b/300 Longest Increasing Subsequence.py @@ -0,0 +1,83 @@ +""" +Given an unsorted array of integers, find the length of longest increasing subsequence. + +For example, +Given [10, 9, 2, 5, 3, 7, 101, 18], +The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that 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? +""" +__author__ = 'Daniel' + + +class Solution(object): + def lengthOfLIS(self, A): + """ + M: min of index last value of LIS of a particular length + :type A: List[int] + :rtype: int + """ + if not A: + return 0 + + n = len(A) + M = [-1 for _ in xrange(n+1)] + l = 1 + M[l] = 0 + for i in xrange(1,n): + if A[i] > A[M[l]]: + l += 1 + M[l] = i + else: + j = self.bin_saerch(M, A, A[i], 1, l+1) + M[j] = i + + return l + + def bin_saerch(self, M, A, t, lo=0, hi=None): + if not hi: hi = len(M) + while lo < hi: + m = (lo+hi)/2 + if A[M[m]] == t: + return m + elif A[M[m]] < t: + lo = m + 1 + else: + hi = m + + return lo + + def lengthOfLIS_dp(self, A): + """ + dp + + let F[i] be the LIS length ends at A[i] + F[i] = max(F[j]+1 for all j < i if A[i] > A[j]) + + avoid max() arg is an empty sequence + + O(n^2) + :type nums: List[int] + :rtype: int + """ + if not A: + return 0 + + n = len(A) + F = [1 for _ in xrange(n)] + maxa = 1 + for i in xrange(1, n): + F[i] = max( + F[j] + 1 if A[i] > A[j] else 1 + for j in xrange(i) + ) + maxa = max(maxa, F[i]) + + return maxa + + +if __name__ == "__main__": + print Solution().lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18]) \ No newline at end of file From d9ec458309b19c8cf2089bf930daee6390d8e555 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 4 Nov 2015 01:18:16 -0800 Subject: [PATCH 085/585] LIS --- 300 Longest Increasing Subsequence.py | 42 +++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/300 Longest Increasing Subsequence.py b/300 Longest Increasing Subsequence.py index ab57891..cb515f7 100644 --- a/300 Longest Increasing Subsequence.py +++ b/300 Longest Increasing Subsequence.py @@ -32,12 +32,12 @@ def lengthOfLIS(self, A): l += 1 M[l] = i else: - j = self.bin_saerch(M, A, A[i], 1, l+1) + j = self.bin_search(M, A, A[i], 1, l+1) M[j] = i return l - def bin_saerch(self, M, A, t, lo=0, hi=None): + def bin_search(self, M, A, t, lo=0, hi=None): if not hi: hi = len(M) while lo < hi: m = (lo+hi)/2 @@ -50,6 +50,44 @@ def bin_saerch(self, M, A, t, lo=0, hi=None): return lo + def lengthOfLIS_output_all(self, A): + """ + Maintain the result of LIS + M: min of index last value of LIS of a particular length + R: result table, store the predecessor idx + :type A: List[int] + :rtype: int + """ + if not A: + return 0 + + n = len(A) + M = [-1 for _ in xrange(n+1)] + R = [-1 for _ in xrange(n)] + l = 1 + M[l] = 0 + for i in xrange(1, n): + if A[i] > A[M[l]]: + l += 1 + M[l] = i + R[i] = M[l-1] + else: + j = self.bin_search(M, A, A[i], 1, l+1) + M[j] = i + R[i] = M[j-1] if j-1 >= 1 else -1 + + cur = M[l] + ret = [] + while True: + ret.append(A[cur]) + if R[cur] == -1: break + cur = R[cur] + + ret = ret[::-1] + print ret + + return l + def lengthOfLIS_dp(self, A): """ dp From 7cb4d8f32b95feea462bca5f95569dcc00ebb0e6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 5 Nov 2015 21:10:42 -0800 Subject: [PATCH 086/585] backtrack --- 301 Remove Invalid Parentheses.py | 71 +++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 301 Remove Invalid Parentheses.py diff --git a/301 Remove Invalid Parentheses.py b/301 Remove Invalid Parentheses.py new file mode 100644 index 0000000..edc5ffd --- /dev/null +++ b/301 Remove Invalid Parentheses.py @@ -0,0 +1,71 @@ +""" +Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results. + +Note: The input string may contain letters other than the parentheses ( and ). + +Examples: +"()())()" -> ["()()()", "(())()"] +"(a)())()" -> ["(a)()()", "(a())()"] +")(" -> [""] + +""" +__author__ = 'Daniel' + + +class Solution(object): + def removeInvalidParentheses(self, s): + """ + :type s: str + :rtype: List[str] + """ + n = len(s) + # solve min count of removal + cnt = 0 + l = 0 + for c in s: + if c == "(": + l += 1 + elif c == ")": + l -= 1 + if l < 0: + l = 0 + cnt += 1 + + cnt += l + ret = [] + self.dfs(s, "", 0, None, 0, cnt, ret) + return ret + + def dfs(self, s, cur, l, removed, i, cnt, ret): + """backtracking, pre-check""" + if l < 0 or cnt < 0 or i > len(s): + return + if i == len(s): + if cnt == 0 and l == 0: + ret.append(cur) + return + + if s[i] in ("(", ")"): + # jump + C = cnt + while i < len(s) and removed and removed == s[i]: + i += 1 + C -= 1 + + if C != cnt: + self.dfs(s, cur, l, removed, i, C, ret) + else: + self.dfs(s, cur, l, s[i], i+1, cnt-1, ret) + + L = l+1 if s[i] == "(" else l-1 + self.dfs(s, cur+s[i], L, None, i+1, cnt, ret) # put + else: + self.dfs(s, cur+s[i], l, None, i+1, cnt, ret) + + +if __name__ == "__main__": + assert Solution().removeInvalidParentheses("(a)())()") == ['(a())()', '(a)()()'] + + + + From 8d2202df59f95a5495531cde872e0a50a16ddad8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 5 Nov 2015 21:24:29 -0800 Subject: [PATCH 087/585] optimize --- 299 Bulls and Cows.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/299 Bulls and Cows.py b/299 Bulls and Cows.py index 24177a3..b9fe21d 100644 --- a/299 Bulls and Cows.py +++ b/299 Bulls and Cows.py @@ -10,27 +10,28 @@ def getHint(self, secret, guess): :type guess: str :rtype: str """ - hm = defaultdict(int) + cnt = defaultdict(int) A = 0 B = 0 for c in secret: - hm[c] += 1 + cnt[c] += 1 for i, v in enumerate(guess): if v == secret[i]: A += 1 - hm[v] -= 1 - if hm[v] < 0: - assert hm[v] == -1 + cnt[v] -= 1 + if cnt[v] < 0: + # revert matched B + assert cnt[v] == -1 B -= 1 - hm[v] = 0 + cnt[v] = 0 - elif v in hm and hm[v] > 0: + elif cnt[v] > 0: B += 1 - hm[v] -= 1 + cnt[v] -= 1 return "%dA%dB" % (A, B) if __name__ == "__main__": - print Solution().getHint("0", "1") + assert Solution().getHint("0", "1") == "0A0B" From 8a53d85f6c7b4fd9bb875afeb112ac0fc826eddc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 5 Nov 2015 21:45:25 -0800 Subject: [PATCH 088/585] tree --- ...inary Tree Longest Consecutive Sequence.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 298 Binary Tree Longest Consecutive Sequence.py diff --git a/298 Binary Tree Longest Consecutive Sequence.py b/298 Binary Tree Longest Consecutive Sequence.py new file mode 100644 index 0000000..9ee68bb --- /dev/null +++ b/298 Binary Tree Longest Consecutive Sequence.py @@ -0,0 +1,79 @@ +""" +Given a binary tree, find the length of the longest consecutive sequence path. + +The path refers to any sequence of nodes from some starting node to any node in the tree along the parent-child +connections. The longest consecutive path need to be from parent to child (cannot be the reverse). + +For example, + 1 + \ + 3 + / \ + 2 4 + \ + 5 +Longest consecutive sequence path is 3-4-5, so return 3. + 2 + \ + 3 + / + 2 + / + 1 +Longest consecutive sequence path is 2-3,not3-2-1, so return 2. + +""" +__author__ = 'Daniel' + + +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def __init__(self): + self.maxa = 0 + + def longestConsecutive(self, root): + self.longest(root) + return self.maxa + + def longest(self, root): + """ + longest ended at root + """ + if not root: + return 0 + + maxa = 1 + l = self.longest(root.left) + r = self.longest(root.right) + if root.left and root.val+1 == root.left.val: + maxa = max(maxa, l+1) + if root.right and root.val+1 == root.right.val: + maxa = max(maxa, r+1) + + self.maxa = max(self.maxa, maxa) + return maxa + + def longestConsecutive_error(self, root): + """ + :type root: TreeNode + :rtype: int + """ + if not root: + return 0 + + maxa = 1 + l = self.longestConsecutive(root.left) + r = self.longestConsecutive(root.right) + maxa = max(maxa, l, r) + if root.left and root.val + 1 == root.left.val: + maxa = max(maxa, l+1) + if root.right and root.val + 1 == root.right.val: + maxa = max(maxa, r+1) + + return maxa \ No newline at end of file From a8f72501624cc4cdb066f239c4e69bdbd75cd8d7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 5 Nov 2015 23:06:16 -0800 Subject: [PATCH 089/585] moveup --- 005 Longest Palindromic Substring.py | 41 ++++++++++++++-------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/005 Longest Palindromic Substring.py b/005 Longest Palindromic Substring.py index 552bfb8..ca1abd6 100644 --- a/005 Longest Palindromic Substring.py +++ b/005 Longest Palindromic Substring.py @@ -5,7 +5,27 @@ __author__ = 'Danyang' -class Solution: +class Solution(object): + def longestPalindrome(self, s): + """ + O(n^2) + :param s: string + :return: string + """ + if not s: + return + n = len(s) + if n == 1: + return s + + ret = s[0] + for i in xrange(0, n): + cur = self.get_palindrome_from_center(s, i, i) # odd length + if len(cur) > len(ret): ret = cur + cur = self.get_palindrome_from_center(s, i, i+1) + if len(cur) > len(ret): ret = cur + return ret + def longestPalindrome_TLE(self, s): """ Algorithm: dp, O(n^2) @@ -75,25 +95,6 @@ def longestPalindrome_TLE2(self, s): return longest - def longestPalindrome(self, s): - """ - :param s: string - :return: string - """ - if not s: - return - length = len(s) - if length == 1: - return s - - longest = s[0] - for i in xrange(0, length): - current = self.get_palindrome_from_center(s, i, i) # odd length - if len(current) > len(longest): longest = current - current = self.get_palindrome_from_center(s, i, i+1) - if len(current) > len(longest): longest = current - return longest - def get_palindrome_from_center(self, s, begin, end): """ # [begin, end] From ea74c5077f27851ec4c204f4dfe25ae1e0c4c405 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 6 Nov 2015 09:23:12 -0800 Subject: [PATCH 090/585] update --- ...inary Tree Longest Consecutive Sequence.py | 79 ------------------- 301 Remove Invalid Parentheses.py | 2 +- 2 files changed, 1 insertion(+), 80 deletions(-) delete mode 100644 298 Binary Tree Longest Consecutive Sequence.py diff --git a/298 Binary Tree Longest Consecutive Sequence.py b/298 Binary Tree Longest Consecutive Sequence.py deleted file mode 100644 index 9ee68bb..0000000 --- a/298 Binary Tree Longest Consecutive Sequence.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -Given a binary tree, find the length of the longest consecutive sequence path. - -The path refers to any sequence of nodes from some starting node to any node in the tree along the parent-child -connections. The longest consecutive path need to be from parent to child (cannot be the reverse). - -For example, - 1 - \ - 3 - / \ - 2 4 - \ - 5 -Longest consecutive sequence path is 3-4-5, so return 3. - 2 - \ - 3 - / - 2 - / - 1 -Longest consecutive sequence path is 2-3,not3-2-1, so return 2. - -""" -__author__ = 'Daniel' - - -class TreeNode(object): - def __init__(self, x): - self.val = x - self.left = None - self.right = None - - -class Solution(object): - def __init__(self): - self.maxa = 0 - - def longestConsecutive(self, root): - self.longest(root) - return self.maxa - - def longest(self, root): - """ - longest ended at root - """ - if not root: - return 0 - - maxa = 1 - l = self.longest(root.left) - r = self.longest(root.right) - if root.left and root.val+1 == root.left.val: - maxa = max(maxa, l+1) - if root.right and root.val+1 == root.right.val: - maxa = max(maxa, r+1) - - self.maxa = max(self.maxa, maxa) - return maxa - - def longestConsecutive_error(self, root): - """ - :type root: TreeNode - :rtype: int - """ - if not root: - return 0 - - maxa = 1 - l = self.longestConsecutive(root.left) - r = self.longestConsecutive(root.right) - maxa = max(maxa, l, r) - if root.left and root.val + 1 == root.left.val: - maxa = max(maxa, l+1) - if root.right and root.val + 1 == root.right.val: - maxa = max(maxa, r+1) - - return maxa \ No newline at end of file diff --git a/301 Remove Invalid Parentheses.py b/301 Remove Invalid Parentheses.py index edc5ffd..c81b88b 100644 --- a/301 Remove Invalid Parentheses.py +++ b/301 Remove Invalid Parentheses.py @@ -37,7 +37,7 @@ def removeInvalidParentheses(self, s): return ret def dfs(self, s, cur, l, removed, i, cnt, ret): - """backtracking, pre-check""" + """backtracking, post-check""" if l < 0 or cnt < 0 or i > len(s): return if i == len(s): From 24d1ac7c41459e84cfbd3d4b70d0f3ddcd0f9dbc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 6 Nov 2015 09:24:00 -0800 Subject: [PATCH 091/585] add recursive --- ...inary Tree Longest Consecutive Sequence.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 298 Binary Tree Longest Consecutive Sequence.py diff --git a/298 Binary Tree Longest Consecutive Sequence.py b/298 Binary Tree Longest Consecutive Sequence.py new file mode 100644 index 0000000..6622200 --- /dev/null +++ b/298 Binary Tree Longest Consecutive Sequence.py @@ -0,0 +1,59 @@ +""" +Premium Question +Recursive +Longest Consecutive Subsequence in BT +""" +__author__ = 'Daniel' + + +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def __init__(self): + self.maxa = 0 + + def longestConsecutive(self, root): + self.longest(root) + return self.maxa + + def longest(self, root): + """ + longest ended at root + """ + if not root: + return 0 + + maxa = 1 + l = self.longest(root.left) + r = self.longest(root.right) + if root.left and root.val+1 == root.left.val: + maxa = max(maxa, l+1) + if root.right and root.val+1 == root.right.val: + maxa = max(maxa, r+1) + + self.maxa = max(self.maxa, maxa) + return maxa + + def longestConsecutive_error(self, root): + """ + :type root: TreeNode + :rtype: int + """ + if not root: + return 0 + + maxa = 1 + l = self.longestConsecutive(root.left) + r = self.longestConsecutive(root.right) + maxa = max(maxa, l, r) + if root.left and root.val + 1 == root.left.val: + maxa = max(maxa, l+1) + if root.right and root.val + 1 == root.right.val: + maxa = max(maxa, r+1) + + return maxa \ No newline at end of file From 4b784580558fbb1f484bd8314e25b085c282ca00 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 6 Nov 2015 13:59:01 -0800 Subject: [PATCH 092/585] permutation --- 060 Permutation Sequence.py | 153 +++++++++++++++++++----------------- 1 file changed, 81 insertions(+), 72 deletions(-) diff --git a/060 Permutation Sequence.py b/060 Permutation Sequence.py index 329f233..9e4ed76 100644 --- a/060 Permutation Sequence.py +++ b/060 Permutation Sequence.py @@ -14,53 +14,67 @@ Note: Given n will be between 1 and 9 inclusive. """ +import math + __author__ = 'Danyang' -class Solution_TLE: - """ - Time Limit Expected - """ - def __init__(self): - self.counter = 0 + +class Solution(object): + def getPermutation(self, n, k): + k -= 1 + + array = range(1, n+1) + k %= math.factorial(n) + ret = [] + for i in xrange(n-1, -1, -1): + idx, k = divmod(k, math.factorial(i)) + ret.append(array.pop(idx)) + + return "".join(map(str, ret)) def getPermutation(self, n, k): """ - dfs, iterate all possibilities + Reverse Contour Expansion + + equation: sum a_i * i! = k :param n: integer :param k: integer :return: String """ - if not n: - return - - sequence = range(1, n+1) - result = self.get_kth_permutation_dfs(sequence, k, []) - return "".join(str(element) for element in result) - + # factorial + fac = [1 for _ in xrange(n)] + for i in xrange(1, n): + fac[i] = fac[i-1]*i + # solve equation + k -= 1 # index starting from 0 + a = [0 for _ in xrange(n)] + for i in xrange(n-1, -1, -1): + a[n-1-i] = k/fac[i] # a[i] = k/fac[i] + k %= fac[i] - def get_kth_permutation_dfs(self, remaining_seq, k, cur): - """ - dfs until find kth permutation, return that permutation, otherwise return None - :param remaining_seq: - :param k: - :param cur: - :return: - """ - if not remaining_seq: - self.counter += 1 - if self.counter==k: - return cur + # post-process + candidate = range(1, n+1) # sorted + visited = [False for _ in xrange(n)] + for ind, val in enumerate(a): + i = 0 # pointer + cnt = 0 # counter + while True: + if visited[i]: + i += 1 + else: + if cnt == val: break + cnt += 1 + i += 1 - for ind, val in enumerate(remaining_seq): - result = self.get_kth_permutation_dfs(remaining_seq[:ind]+remaining_seq[ind+1:], k, cur+[val]) - if result: return result + a[ind] = candidate[i] + visited[i] = True + return "".join(map(str, a)) -class Solution: def getPermutation_complicated(self, n, k): """ - Mathematics, reference: http://fisherlei.blogspot.sg/2013/04/leetcode-permutation-sequence-solution.html + Mathematics Reversed Contour Expansion A = [1, 2, ..., n], where A's index starts from 0 @@ -68,8 +82,7 @@ def getPermutation_complicated(self, n, k): [a0, a1, a2, ..., an-1] since [a1, a3, ..., an-1] has (n-1)! permutations, - if k<(n-1)!, a0 = 1 (first element in array), else a0=k/(n-1)!+1 (subsequent items) - thus a0 = A[k/(n-1)!] + if k < (n-1)!, a0 = A[0] (first element in array), else a0 = A[k/(n-1)!] (subsequent items) recursively, (or iteratively) a0 = A[k0/(n-1)!], where k0 = k @@ -87,7 +100,6 @@ def getPermutation_complicated(self, n, k): for i in xrange(1, n): factorial *= i - result = [] array = range(1, n+1) for i in reversed(xrange(1, n)): @@ -102,54 +114,51 @@ def getPermutation_complicated(self, n, k): return "".join(str(element) for element in result) + +class Solution_TLE: + """ + Time Limit Expected + """ + + def __init__(self): + self.counter = 0 + def getPermutation(self, n, k): """ - Reverse Contour Expansion - - equation: sum a_i * i! = k + dfs, iterate all possibilities :param n: integer :param k: integer :return: String """ - # factorial - fac = [1 for _ in xrange(n)] - for i in xrange(1, n): - fac[i] = fac[i-1]*i - - # solve equation - k -= 1 # index starting from 0 - a = [0 for _ in xrange(n)] - for i in xrange(n-1, -1, -1): - a[n-1-i] = k/fac[i] # a[i] = k/fac[i] - k %= fac[i] - - # post-process - candidate = range(1, n+1) # sorted - visited = [False for _ in xrange(n)] - for ind, val in enumerate(a): - i = 0 # pointer - cnt = 0 # counter - while True: - if visited[i]: - i += 1 - else: - if cnt==val: break - cnt += 1 - i += 1 - - a[ind] = candidate[i] - visited[i] = True - - return "".join(map(str, a)) + if not n: + return + sequence = range(1, n+1) + result = self.get_kth_permutation_dfs(sequence, k, []) + return "".join(str(element) for element in result) + def get_kth_permutation_dfs(self, remaining_seq, k, cur): + """ + dfs until find kth permutation, return that permutation, otherwise return None + :param remaining_seq: + :param k: + :param cur: + :return: + """ + if not remaining_seq: + self.counter += 1 + if self.counter == k: + return cur + for ind, val in enumerate(remaining_seq): + result = self.get_kth_permutation_dfs(remaining_seq[:ind]+remaining_seq[ind+1:], k, cur+[val]) + if result: return result -if __name__=="__main__": - assert Solution().getPermutation(4, 6)=="1432" - assert Solution().getPermutation(2, 2)=="21" - assert Solution().getPermutation(3, 1)=="123" - assert Solution().getPermutation(3, 5)=="312" +if __name__ == "__main__": + assert Solution().getPermutation(4, 6) == "1432" + assert Solution().getPermutation(2, 2) == "21" + assert Solution().getPermutation(3, 1) == "123" + assert Solution().getPermutation(3, 5) == "312" print Solution().getPermutation(9, 171669) \ No newline at end of file From 2a204fb5ac0e83144fd9d6ffd3f01c8e19b53ef1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 7 Nov 2015 11:06:15 -0800 Subject: [PATCH 093/585] update --- 097 Interleaving String.py | 61 +++++++++---------- 153 Find Minimum in Rotated Sorted Array.py | 40 ++++++------ ...Find Minimum in Rotated Sorted Array II.py | 46 +++++++------- 3 files changed, 77 insertions(+), 70 deletions(-) diff --git a/097 Interleaving String.py b/097 Interleaving String.py index 2b1c35f..f2b3723 100644 --- a/097 Interleaving String.py +++ b/097 Interleaving String.py @@ -10,27 +10,9 @@ When s3 = "aadbbbaccc", return false. """ __author__ = 'Danyang' -class Solution: - def isInterleave_TLE(self, s1, s2, s3): - """ - dfs - Time Limit Exceeded - :param s1: - :param s2: - :param s3: - :return: boolean - """ - if not s3: - return True - letter = s3[0] - if s1 and s1[0]==letter: - if self.isInterleave(s1[1:], s2, s3[1:]): - return True - if s2 and s2[0]==letter: - if self.isInterleave(s1, s2[1:], s3[1:]): - return True - return False + +class Solution(object): def isInterleave(self, s1, s2, s3): """ dfs @@ -72,7 +54,7 @@ def isInterleave(self, s1, s2, s3): """ m = len(s1) n = len(s2) - if m+n!=len(s3): + if m+n != len(s3): return False dp = [[False for _ in xrange(n+1)] for _ in xrange(m+1)] @@ -80,25 +62,42 @@ def isInterleave(self, s1, s2, s3): # initialize boundary conditions dp[0][0] = True for i in xrange(1, m+1): - dp[i][0] = dp[i-1][0] and s3[i+0-1]==s1[i-1] + dp[i][0] = dp[i-1][0] and s3[i+0-1] == s1[i-1] for j in xrange(1, n+1): - dp[0][j] = dp[0][j-1] and s3[0+j-1]==s2[j-1] + dp[0][j] = dp[0][j-1] and s3[0+j-1] == s2[j-1] # calculating for i in xrange(1, m+1): for j in xrange(1, n+1): if not dp[i][j]: - dp[i][j] = dp[i-1][j] and s3[i+j-1]==s1[i-1] + dp[i][j] = dp[i-1][j] and s3[i+j-1] == s1[i-1] if not dp[i][j]: - dp[i][j] = dp[i][j-1] and s3[i+j-1]==s2[j-1] + dp[i][j] = dp[i][j-1] and s3[i+j-1] == s2[j-1] return dp[-1][-1] + def isInterleave_TLE(self, s1, s2, s3): + """ + dfs + Time Limit Exceeded + :param s1: + :param s2: + :param s3: + :return: boolean + """ + if not s3: + return True + letter = s3[0] + if s1 and s1[0] == letter: + if self.isInterleave(s1[1:], s2, s3[1:]): + return True + if s2 and s2[0] == letter: + if self.isInterleave(s1, s2[1:], s3[1:]): + return True + return False - - -if __name__=="__main__": - assert Solution().isInterleave("aa", "ab", "abaa")==True - assert Solution().isInterleave("aabcc", "dbbca", "aadbbcbcac")==True - assert Solution().isInterleave("aabcc", "dbbca", "aadbbbaccc")==False \ No newline at end of file +if __name__ == "__main__": + assert Solution().isInterleave("aa", "ab", "abaa") == True + assert Solution().isInterleave("aabcc", "dbbca", "aadbbcbcac") == True + assert Solution().isInterleave("aabcc", "dbbca", "aadbbbaccc") == False \ No newline at end of file diff --git a/153 Find Minimum in Rotated Sorted Array.py b/153 Find Minimum in Rotated Sorted Array.py index 473d82e..fd8bc86 100644 --- a/153 Find Minimum in Rotated Sorted Array.py +++ b/153 Find Minimum in Rotated Sorted Array.py @@ -7,32 +7,36 @@ You may assume no duplicate exists in the array. """ +import sys + __author__ = 'Danyang' -class Solution: - def findMin(self, num): + + +class Solution(object): + def findMin(self, A): """ similar to find target in rotated sorted array - :type num: list - :param num: a list of integer + :type A: list + :param A: a list of integer :return: an integer """ - start = 0 - end = len(num) - mini = 1<<32 - while startnum[mid] A[mid] < A[hi-1]: + hi = mid else: - start = mid+1 + lo = mid+1 return mini -if __name__=="__main__": + +if __name__ == "__main__": num = [7, 1, 2, 3, 4, 5, 6] - print Solution().findMin(num) + assert Solution().findMin(num) == 1 diff --git a/154 Find Minimum in Rotated Sorted Array II.py b/154 Find Minimum in Rotated Sorted Array II.py index cfecb0e..2a1de02 100644 --- a/154 Find Minimum in Rotated Sorted Array II.py +++ b/154 Find Minimum in Rotated Sorted Array II.py @@ -11,34 +11,38 @@ The array may contain duplicates. """ +import sys + __author__ = 'Danyang' -class Solution: - def findMin(self, num): + + +class Solution(object): + def findMin(self, A): """ similar to find target in rotated sorted array - :type num: list - :param num: a list of integer + :type A: list + :param A: a list of integer :return: an integer """ - start = 0 - end = len(num) - mini = 1<<32 - while startnum[mid]<=num[end-1]: - end = mid - else: - start = mid+1 + lo = 0 + hi = len(A) + mini = sys.maxint + while lo < hi: + mid = (lo+hi)/2 + mini = min(mini, A[mid]) + if A[lo] == A[mid]: # JUMP + lo += 1 + elif A[lo] < A[mid] <= A[hi-1]: + return min(mini, A[lo]) + elif A[lo] > A[mid] <= A[hi-1]: # trough + hi = mid + else: # peak + lo = mid+1 return mini -if __name__=="__main__": + +if __name__ == "__main__": num = [7, 1, 2, 2, 3, 4, 5, 6] - print Solution().findMin(num) + assert Solution().findMin(num) == 1 From 9381d0b0c501aca816d4a0c734f42b0596cefbd1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 7 Nov 2015 15:33:57 -0800 Subject: [PATCH 094/585] update lru --- 146 LRU Cache.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/146 LRU Cache.py b/146 LRU Cache.py index 519e515..62ed86e 100644 --- a/146 LRU Cache.py +++ b/146 LRU Cache.py @@ -7,7 +7,83 @@ should invalidate the least recently used item before inserting a new item. """ __author__ = 'Danyang' -class LRUCache: + + +class Node(object): + def __init__(self, key, val): + self.key = key + self.val = val + self.pre, self.next = None, None + + +class LRUCache(object): + def __init__(self, capacity): + self.cap = capacity + self.map = {} # key to node + self.head = None + self.tail = None + + def get(self, key): + if key in self.map: + cur = self.map[key] + self._elevate(cur) + return cur.val + + return -1 + + def set(self, key, value): + if key in self.map: + cur = self.map[key] + cur.val = value + self._elevate(cur) + else: + cur = Node(key, value) + self.map[key] = cur + self._appendleft(cur) + + if len(self.map) > self.cap: + last = self._pop() + del self.map[last.key] + + # doubly linked-list operations only + def _appendleft(self, cur): + """Normal or initially empty""" + if not self.head and not self.tail: + self.head = cur + self.tail = cur + return + + head = self.head + cur.next, cur.pre, head.pre = head, None, cur + self.head = cur + + def _pop(self): + """Normal or resulting empty""" + last = self.tail + if self.head == self.tail: + self.head, self.tail = None, None + return last + + pre = last.pre + pre.next = None + self.tail = pre + return last + + def _elevate(self, cur): + """Head, Tail, Middle""" + pre, nxt = cur.pre, cur.next + if not pre: + return + elif not nxt: + assert self.tail == cur + self._pop() + else: + pre.next, nxt.pre = nxt, pre + + self._appendleft(cur) + + +class LRUCache_TLE(object): def __init__(self, capacity): self.capacity = capacity self.q = [] # order by key @@ -34,7 +110,7 @@ def set(self, key, value): self.q.remove(key) self.q.insert(0, key) else: - if len(self.q)+1<=self.capacity: + if len(self.q)+1 <= self.capacity: self.q.insert(0, key) else: self.dic.pop(self.q.pop()) From a752cf8928e8eeecdf0fd30e49567e56c3f2797a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 7 Nov 2015 23:07:58 -0800 Subject: [PATCH 095/585] update --- 300 Longest Increasing Subsequence.py | 45 ++++++++++++++------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/300 Longest Increasing Subsequence.py b/300 Longest Increasing Subsequence.py index cb515f7..8955953 100644 --- a/300 Longest Increasing Subsequence.py +++ b/300 Longest Increasing Subsequence.py @@ -16,7 +16,7 @@ class Solution(object): def lengthOfLIS(self, A): """ - M: min of index last value of LIS of a particular length + MIN: min of index last value of LIS of a particular length :type A: List[int] :rtype: int """ @@ -24,16 +24,16 @@ def lengthOfLIS(self, A): return 0 n = len(A) - M = [-1 for _ in xrange(n+1)] + MIN = [-1 for _ in xrange(n+1)] l = 1 - M[l] = 0 + MIN[l] = 0 for i in xrange(1,n): - if A[i] > A[M[l]]: + if A[i] > A[MIN[l]]: l += 1 - M[l] = i + MIN[l] = i else: - j = self.bin_search(M, A, A[i], 1, l+1) - M[j] = i + j = self.bin_search(MIN, A, A[i], 1, l+1) + MIN[j] = i return l @@ -53,8 +53,8 @@ def bin_search(self, M, A, t, lo=0, hi=None): def lengthOfLIS_output_all(self, A): """ Maintain the result of LIS - M: min of index last value of LIS of a particular length - R: result table, store the predecessor idx + MIN: min of index last value of LIS of a particular length + RET: result table, store the predecessor's idx (optional) :type A: List[int] :rtype: int """ @@ -62,26 +62,29 @@ def lengthOfLIS_output_all(self, A): return 0 n = len(A) - M = [-1 for _ in xrange(n+1)] - R = [-1 for _ in xrange(n)] + MIN = [-1 for _ in xrange(n+1)] + RET = [-1 for _ in xrange(n)] l = 1 - M[l] = 0 + MIN[l] = 0 for i in xrange(1, n): - if A[i] > A[M[l]]: + if A[i] > A[MIN[l]]: l += 1 - M[l] = i - R[i] = M[l-1] + MIN[l] = i + + RET[i] = MIN[l-1] # (optional) else: - j = self.bin_search(M, A, A[i], 1, l+1) - M[j] = i - R[i] = M[j-1] if j-1 >= 1 else -1 + j = self.bin_search(MIN, A, A[i], 1, l+1) + MIN[j] = i + + RET[i] = MIN[j-1] if j-1 >= 1 else -1 # (optional) - cur = M[l] + # build the LIS (optional) + cur = MIN[l] ret = [] while True: ret.append(A[cur]) - if R[cur] == -1: break - cur = R[cur] + if RET[cur] == -1: break + cur = RET[cur] ret = ret[::-1] print ret From a85d9fe3b9af218fcb85b2e67c6b55054bb221f4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 9 Nov 2015 00:41:14 -0800 Subject: [PATCH 096/585] search 2D mat --- 074 Search a 2D Matrix.py | 58 ++++++++++++++-------------- 200 Number of Islands.py | 8 ++-- 240 Search a 2D Matrix II.py | 75 ++++++++++++++++++++++++++---------- 3 files changed, 88 insertions(+), 53 deletions(-) diff --git a/074 Search a 2D Matrix.py b/074 Search a 2D Matrix.py index 7bb2dd1..1e32925 100644 --- a/074 Search a 2D Matrix.py +++ b/074 Search a 2D Matrix.py @@ -15,48 +15,50 @@ Given target = 3, return true. """ __author__ = 'Danyang' -class Solution: - def searchMatrix(self, matrix, target): + + +class Solution(object): + def searchMatrix(self, mat, target): """ binary search. Two exactly the same binary search algorithm - :param matrix: a list of lists of integers + :param mat: a list of lists of integers :param target: an integer :return: a boolean """ - if not matrix: + if not mat: return False - m = len(matrix) - n = len(matrix[0]) + m = len(mat) + n = len(mat[0]) # binary search - start = 0 - end = m # [0, m) - while startmatrix[mid][0]: - start = mid+1 + elif mat[mid][0] < target: + lo = mid+1 + else: + hi = mid - - lst = matrix[end] if matrix[end][0]<=target else matrix[start] # positioning ! + lst = mat[lo-1] # <= # binary search - start = 0 - end = n # [0, n) - while startlst[mid]: - start = mid+1 + elif lst[mid] < target: + lo = mid+1 + else: + hi = mid return False -if __name__=="__main__": - assert Solution().searchMatrix([[1], [3]], 3)==True \ No newline at end of file + +if __name__ == "__main__": + assert Solution().searchMatrix([[1], [3]], 3) == True \ No newline at end of file diff --git a/200 Number of Islands.py b/200 Number of Islands.py index c18ef72..2b04ab2 100644 --- a/200 Number of Islands.py +++ b/200 Number of Islands.py @@ -57,10 +57,10 @@ def dfs(self, grid, i, j, visited): visited[i][j] = True for dir in self.dirs: - n_i = i+dir[0] - n_j = j+dir[1] - if 0 <= n_i < m and 0 <= n_j < n and not visited[n_i][n_j] and grid[n_i][n_j] == "1": - self.dfs(grid, n_i, n_j, visited) + I = i+dir[0] + J = j+dir[1] + if 0 <= I < m and 0 <= J < n and not visited[I][J] and grid[I][J] == "1": + self.dfs(grid, I, J, visited) if __name__ == "__main__": diff --git a/240 Search a 2D Matrix II.py b/240 Search a 2D Matrix II.py index 978b23d..fc32e73 100644 --- a/240 Search a 2D Matrix II.py +++ b/240 Search a 2D Matrix II.py @@ -21,33 +21,64 @@ __author__ = 'Daniel' -class Solution: - def searchMatrix(self, matrix, target): +class Solution(object): + def searchMatrix(self, mat, target): """ + Manhattan work + O(m+n) eliminate a row or a column at a time + Practically: 112 ms + + :type mat: list[int][int] + :type target: int + :rtype: bool + """ + m = len(mat) + n = len(mat[0]) + + i = 0 + j = n-1 + while i < m and 0 <= j: + if mat[i][j] == target: + return True + elif mat[i][j] > target: + j -= 1 + else: + i += 1 + + return False + + +class SolutionBinSearch(object): + def searchMatrix(self, mat, target): + """ + Binary search + Multiple round of binary search + possible to swap m and n, depends on the size + O(m log n) or O(n log m) + Practically: 204 ms - :type matrix: list[int][int] + :type mat: list[int][int] :type target: int :rtype: bool """ - try: - m = len(matrix) - n = len(matrix[0]) - - lst = [matrix[i][0] for i in xrange(m)] - row_by_first = self.bisect(lst, target) - lst = [matrix[i][-1] for i in xrange(m)] - row_by_last = self.bisect(lst, target, False) - for i in range(row_by_first, row_by_last-1, -1): - col = self.bisect(matrix[i], target) - if matrix[i][col] == target: - return True - - return False - except IndexError: - return False - - def bisect(self, A, t, lower=True): + m = len(mat) + n = len(mat[0]) + + col = [mat[i][0] for i in xrange(m)] + row_by_first = self.bin_search(col, target) + + col = [mat[i][-1] for i in xrange(m)] + row_by_last = self.bin_search(col, target, False) + + for i in range(row_by_first, row_by_last-1, -1): + col = self.bin_search(mat[i], target) + if mat[i][col] == target: + return True + + return False + + def bin_search(self, A, t, lower=True): lo = 0 hi = len(A) while lo < hi: @@ -58,6 +89,7 @@ def bisect(self, A, t, lower=True): lo = mid+1 else: hi = mid + if lower: return lo-1 else: @@ -65,3 +97,4 @@ def bisect(self, A, t, lower=True): if __name__ == "__main__": assert Solution().searchMatrix([[1, 4], [2, 5]], 4) == True + assert SolutionBinSearch().searchMatrix([[1, 4], [2, 5]], 4) == True From 26b962711990939fe8c7333326e5212fa3fbaf03 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 9 Nov 2015 12:03:50 -0800 Subject: [PATCH 097/585] interval --- 055 Merge Intervals.py | 84 +++++++++++++++++++++--------------------- 057 Insert Interval.py | 59 +++++++++++++++++++---------- 2 files changed, 81 insertions(+), 62 deletions(-) diff --git a/055 Merge Intervals.py b/055 Merge Intervals.py index c247696..554aefb 100644 --- a/055 Merge Intervals.py +++ b/055 Merge Intervals.py @@ -6,68 +6,66 @@ return [1,6],[8,10],[15,18]. """ __author__ = 'Danyang' + + # Definition for an interval. -class Interval: +class Interval(object): def __init__(self, s=0, e=0): self.start = s self.end = e -class Solution: - def merge_error(self, intervals): + +class Solution(object): + def merge(self, itvls): """ scanning. No algorithm math - :param intervals: a list of Interval + :param itvls: a list of Interval :return: a list of Interval """ - if not intervals: + if not itvls: return [] - result = [] - result.append(intervals[0]) - for interval in intervals[1:]: - if result[-1].end e, itvls) + if len(left)+len(right) != len(itvls): + s = min(s, itvls[len(left)].start) + e = max(e, itvls[-len(right)-1].end) + + return left + [Interval(s, e)] + right + + def insert_itr(self, itvls, newItvl): + """ + iterator TODO """ - :param intervals: a list of Intervals - :param newInterval: a Interval + +class SolutionSlow(object): + def insert(self, itvls, newItvl): + """ + :param itvls: a list of Intervals + :param newItvl: a Interval :return: a list of Interval """ - return self.merge(intervals+[newInterval]) + return self.merge(itvls+[newItvl]) - def merge(self, intervals): + def merge(self, itvls): """ sort first by .start then decide whether to extend the .end - :param intervals: list of Interval + :param itvls: list of Interval :return: list of Interval """ - intervals.sort(cmp=lambda a, b: a.start - b.start) + itvls.sort(cmp=lambda a, b: a.start - b.start) - result = [intervals[0]] - for cur in intervals[1:]: - pre = result[-1] - if cur.start<=pre.end: # overlap + ret = [itvls[0]] + for cur in itvls[1:]: + pre = ret[-1] + if cur.start <= pre.end: # overlap pre.end = max(pre.end, cur.end) else: - result.append(cur) + ret.append(cur) - return result + return ret -if __name__=="__main__": - lst = [[1,2],[3,5],[6,7],[8,10],[12,16]] - insert = [4,9] +if __name__ == "__main__": + lst = [[1, 2], [3, 5], [6, 7], [8, 10], [12, 16]] + insert = [4, 9] lst_interval = [] for item in lst: lst_interval.append(Interval(item[0], item[1])) From 5e01f59aee0e4acdebd31272f91342290e4a8ffb Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 12 Nov 2015 23:36:33 -0800 Subject: [PATCH 098/585] cantor --- 030 Next Permutation.py | 2 +- 060 Permutation Sequence.py | 4 ++-- 094 Unique Binary Search Trees.py | 22 ++++++++++------------ 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/030 Next Permutation.py b/030 Next Permutation.py index a74637a..c6a3202 100644 --- a/030 Next Permutation.py +++ b/030 Next Permutation.py @@ -15,7 +15,7 @@ class Solution: def nextPermutation(self, num): """ Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers. - Unable to use Contour Expansion due to duplicates + Unable to use Cantor Expansion due to duplicates Classic algorithm in STL math problem; algorithm: http://fisherlei.blogspot.sg/2012/12/leetcode-next-permutation.html diff --git a/060 Permutation Sequence.py b/060 Permutation Sequence.py index 9e4ed76..3ac410b 100644 --- a/060 Permutation Sequence.py +++ b/060 Permutation Sequence.py @@ -34,7 +34,7 @@ def getPermutation(self, n, k): def getPermutation(self, n, k): """ - Reverse Contour Expansion + Reverse Cantor Expansion equation: sum a_i * i! = k :param n: integer @@ -75,7 +75,7 @@ def getPermutation(self, n, k): def getPermutation_complicated(self, n, k): """ Mathematics - Reversed Contour Expansion + Reversed Cantor Expansion A = [1, 2, ..., n], where A's index starts from 0 Suppose for n element, the k-th permutation is: diff --git a/094 Unique Binary Search Trees.py b/094 Unique Binary Search Trees.py index cb0f7be..021ddc8 100644 --- a/094 Unique Binary Search Trees.py +++ b/094 Unique Binary Search Trees.py @@ -10,8 +10,12 @@ / / \ \ 2 1 2 3 """ +import math + __author__ = 'Danyang' -class Solution: + + +class Solution(object): def numTrees_math(self, n): """ number of unique binary search tree @@ -22,14 +26,8 @@ def numTrees_math(self, n): :param n: integer :return: integer """ - return self.factorial(2*n)/(self.factorial(n)*self.factorial(n)) -self.factorial(2*n)/( - self.factorial(n+1)*self.factorial(n-1)) - - def factorial(self, n): - factorial = 1 - for i in range(n): - factorial *= i+1 - return factorial + return math.factorial(2*n)/(math.factorial(n)*math.factorial(n))-math.factorial(2*n)/( + math.factorial(n+1)*math.factorial(n-1)) def numTrees(self, n): """ @@ -47,7 +45,7 @@ def numTrees(self, n): :param n: integer :return: integer """ - if n<2: + if n < 2: return n dp = [0 for _ in xrange(n+1)] @@ -58,5 +56,5 @@ def numTrees(self, n): return dp[-1] -if __name__=="__main__": - assert Solution().numTrees(100)==Solution().numTrees_math(100) \ No newline at end of file +if __name__ == "__main__": + assert Solution().numTrees(100) == Solution().numTrees_math(100) \ No newline at end of file From 2ae311cc82a9f362c35e76ca537805e555caeb10 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 14 Nov 2015 22:30:06 -0800 Subject: [PATCH 099/585] dp, union-find --- 303 Range Sum Query - Immutable.py | 37 ++++++++++++++ 304 Range Sum Query 2D - Immutable.py | 55 ++++++++++++++++++++ 305 Number of Islands II.py | 74 +++++++++++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 303 Range Sum Query - Immutable.py create mode 100644 304 Range Sum Query 2D - Immutable.py create mode 100644 305 Number of Islands II.py diff --git a/303 Range Sum Query - Immutable.py b/303 Range Sum Query - Immutable.py new file mode 100644 index 0000000..818fa75 --- /dev/null +++ b/303 Range Sum Query - Immutable.py @@ -0,0 +1,37 @@ +""" +Given an integer array nums, find the sum of the elements between indices i and j (i <= j), inclusive. + +Example: +Given nums = [-2, 0, 3, -5, 2, -1] + +sumRange(0, 2) -> 1 +sumRange(2, 5) -> -1 +sumRange(0, 5) -> -3 +Note: +You may assume that the array does not change. +There are many calls to sumRange function. + +""" +__author__ = 'Daniel' + + +class NumArray(object): + def __init__(self, nums): + """ + initialize your data structure here. + dp + :type nums: List[int] + """ + n = len(nums) + self.F = [0 for _ in xrange(n+1)] + for i in xrange(1, n+1): + self.F[i] = self.F[i-1] + nums[i-1] + + def sumRange(self, i, j): + """ + sum of elements nums[i..j], inclusive. + :type i: int + :type j: int + :rtype: int + """ + return self.F[j+1] - self.F[i] \ No newline at end of file diff --git a/304 Range Sum Query 2D - Immutable.py b/304 Range Sum Query 2D - Immutable.py new file mode 100644 index 0000000..49671b5 --- /dev/null +++ b/304 Range Sum Query 2D - Immutable.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +""" +Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, +col1) and lower right corner (row2, col2). + +Range Sum Query 2D +The above rectangle (with the red border) is defined by (row1, col1) = (2, 1) and (row2, col2) = (4, 3), which contains +sum = 8. + +Example: +Given matrix = [ + [3, 0, 1, 4, 2], + [5, 6, 3, 2, 1], + [1, 2, 0, 1, 5], + [4, 1, 0, 1, 7], + [1, 0, 3, 0, 5] +] + +sumRegion(2, 1, 4, 3) -> 8 +sumRegion(1, 1, 2, 2) -> 11 +sumRegion(1, 2, 2, 4) -> 12 +Note: +You may assume that the matrix does not change. +There are many calls to sumRegion function. +You may assume that row1 ≤ row2 and col1 ≤ col2. +""" +__author__ = 'Daniel' + + +class NumMatrix(object): + def __init__(self, matrix): + """ + initialize your data structure here. + dp F[i][j] = F[i-1][j]+F[i][j-1]-F[i-1][j-1]+mat[i][j] + :type matrix: List[List[int]] + """ + m = len(matrix) + if m == 0: + self.F = None + return + + n = len(matrix[0]) + self.F = [[0 for _ in xrange(n+1)] for _ in xrange(m+1)] + for i in xrange(1, m+1): + for j in xrange(1, n+1): + self.F[i][j] = self.F[i-1][j]+self.F[i][j-1]-self.F[i-1][j-1]+matrix[i-1][j-1] + + def sumRegion(self, row1, col1, row2, col2): + """ + sum of elements matrix[(row1,col1)..(row2,col2)], inclusive. + """ + if not self.F: + return 0 + + return self.F[row2+1][col2+1] - self.F[row2+1][col1] - self.F[row1][col2+1] + self.F[row1][col1] \ No newline at end of file diff --git a/305 Number of Islands II.py b/305 Number of Islands II.py new file mode 100644 index 0000000..d48a0ef --- /dev/null +++ b/305 Number of Islands II.py @@ -0,0 +1,74 @@ +""" +Premium Question +""" +from collections import namedtuple + +__author__ = 'Daniel' + + +class UnionFind(object): + """ + Weighted Union Find with path compression + """ + def __init__(self, rows, cols): + # hashing will cause TLE; use direct array access instead + self.pi = [-1 for _ in xrange(rows*cols)] # item -> pi + self.sz = [-1 for _ in xrange(rows*cols)] # root -> size + self.count = 0 + + def add(self, item): + if self.pi[item] == -1: + self.pi[item] = item + self.sz[item] = 1 + self.count += 1 + + def union(self, a, b): + pi1 = self._pi(a) + pi2 = self._pi(b) + + if pi1 != pi2: + if self.sz[pi1] > self.sz[pi2]: + pi1, pi2 = pi2, pi1 + + self.pi[pi1] = pi2 + self.sz[pi2] += self.sz[pi1] + self.count -= 1 + + def _pi(self, item): + """ + Get root with path compression + """ + pi = self.pi[item] + if item != pi: + self.pi[item] = self._pi(pi) + + return self.pi[item] + + +Op = namedtuple('Op', 'r c') # row col + + +class Solution: + def __init__(self): + self.dirs = ((-1, 0), (1, 0), (0, -1), (0, 1)) + + def numIslands2(self, n, m, operators): + rows = n + cols = m + unroll = lambda x, y: x*cols + y # hash will be slower + mat = [[0 for _ in xrange(cols)] for _ in xrange(rows)] + uf = UnionFind(rows, cols) + ret = [] + for op in operators: + op = Op(r=op[0], c=op[1]) + uf.add(unroll(op.r, op.c)) + mat[op.r][op.c] = 1 + for dir in self.dirs: + x1 = op.r+dir[0] + y1 = op.c+dir[1] + if 0 <= x1 < rows and 0 <= y1 < cols and mat[x1][y1] == 1: + uf.union(unroll(op.r, op.c), unroll(x1, y1)) + + ret.append(uf.count) + + return ret \ No newline at end of file From c69a5b239df259ed885c6669c1ced93175db4347 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 18 Nov 2015 20:00:55 -0800 Subject: [PATCH 100/585] backtracking --- 306 Additive Number.py | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 306 Additive Number.py diff --git a/306 Additive Number.py b/306 Additive Number.py new file mode 100644 index 0000000..9fb1f98 --- /dev/null +++ b/306 Additive Number.py @@ -0,0 +1,53 @@ +""" +Additive number is a positive integer whose digits can form additive sequence. + +A valid additive sequence should contain at least three numbers. Except for the first two numbers, each subsequent +number in the sequence must be the sum of the preceding two. + +For example: +"112358" is an additive number because the digits can form an additive sequence: 1, 1, 2, 3, 5, 8. + +1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8 +"199100199" is also an additive number, the additive sequence is: 1, 99, 100, 199. +1 + 99 = 100, 99 + 100 = 199 +Note: Numbers in the additive sequence cannot have leading zeros, so sequence 1, 2, 03 or 1, 02, 3 is invalid. + +Given a string represents an integer, write a function to determine if it's an additive number. +""" +__author__ = 'Daniel' + + +class Solution(object): + def isAdditiveNumber(self, num): + """ + Backtracking + :type num: str + :rtype: bool + """ + n = len(num) + for i in xrange(1, n): + for j in xrange(i, n): + if self.predicate(num, 0, i, j): + return True + + return False + + def predicate(self, s, b, i, j): + n1 = s[b:i] + n2 = s[i:j] + + if b != 0 and j == len(s): + return True + if not n1 or not n2: + return False + if len(n1) > 1 and n1[0] == '0' or len(n2) > 1 and n2[0] == '0': + return False + + n3 = str(int(n1)+int(n2)) + J = j+len(n3) + if s[j:J] == n3: + return self.predicate(s, i, j, J) + + +if __name__ == "__main__": + assert Solution().isAdditiveNumber("12012122436") \ No newline at end of file From 95c11932a3e3951993c18c398d9d8c7f97897f55 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 18 Nov 2015 20:34:37 -0800 Subject: [PATCH 101/585] binary index tree --- 307 Range Sum Query - Mutable.py | 70 ++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 307 Range Sum Query - Mutable.py diff --git a/307 Range Sum Query - Mutable.py b/307 Range Sum Query - Mutable.py new file mode 100644 index 0000000..473b346 --- /dev/null +++ b/307 Range Sum Query - Mutable.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +""" +Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. + +The update(i, val) function modifies nums by updating the element at index i to val. +Example: +Given nums = [1, 3, 5] + +sumRange(0, 2) -> 9 +update(1, 2) +sumRange(0, 2) -> 8 +Note: +The array is only modifiable by the update function. +You may assume the number of calls to update and sumRange function is distributed evenly. +""" +__author__ = 'Daniel' + + +class BinaryIndexTree(object): + def __init__(self, nums): + """BIT 0 is dummy root""" + n = len(nums) + self.nums = [0 for _ in xrange(n+1)] + self.N = [0 for _ in xrange(n+1)] + for i, v in enumerate(nums): + self.set(i+1, v) + + def _lowbit(self, a): + return a & -a + + def set(self, i, val): + diff = val - self.nums[i] + self.nums[i] = val + while i < len(self.N): + self.N[i] += diff + i += self._lowbit(i) + + def get(self, i): + ret = 0 + while i > 0: + ret += self.N[i] + i -= self._lowbit(i) + + return ret + + +class NumArray(object): + def __init__(self, nums): + """ + initialize your data structure here. + :type nums: List[int] + """ + self.bit = BinaryIndexTree(nums) + + def update(self, i, val): + """ + :type i: int + :type val: int + :rtype: int + """ + self.bit.set(i+1, val) + + def sumRange(self, i, j): + """ + sum of elements nums[i..j], inclusive. + :type i: int + :type j: int + :rtype: int + """ + return self.bit.get(j+1)-self.bit.get(i) \ No newline at end of file From 6e4f1ce6ea539f5d9b9a53740d24cd6575f25735 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 21 Nov 2015 19:10:17 -0800 Subject: [PATCH 102/585] bin search and projection --- ...allest Rectangle Enclosing Black Pixels.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 302 Smallest Rectangle Enclosing Black Pixels.py diff --git a/302 Smallest Rectangle Enclosing Black Pixels.py b/302 Smallest Rectangle Enclosing Black Pixels.py new file mode 100644 index 0000000..14fe263 --- /dev/null +++ b/302 Smallest Rectangle Enclosing Black Pixels.py @@ -0,0 +1,39 @@ +""" +Premium Question +Projection and bisect +""" +import bisect +__author__ = 'Daniel' + + +class Solution(object): + def minArea(self, image, x, y): + """ + :type image: List[List[str]] + :type x: int + :type y: int + :rtype: int + """ + m, n = len(image), len(image[0]) + yaxis = [ + 1 if any(image[i][j] == "1" for i in xrange(m)) else 0 + for j in xrange(n) + ] + xaxis = [ + 1 if any(image[i][j] == "1" for j in xrange(n)) else 0 + for i in xrange(m) + ] + + y_lo = bisect.bisect_left(yaxis, 1, 0, y) + y_hi = bisect.bisect_left(map(lambda e: 1^e, yaxis), 1, y) # bisect must be sorted + x_lo = bisect.bisect_left(xaxis, 1, 0, x) + x_hi = bisect.bisect_left(map(lambda e: 1^e, xaxis), 1, x) + return (y_hi-y_lo)*(x_hi-x_lo) + +if __name__ == "__main__": + image = [ + "00", + "10", + ] + + assert Solution().minArea(image, 1, 0) == 1 \ No newline at end of file From 9bc7c2ec792d2ec5d68a5d76705e6a0f23a7c06b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 10 Dec 2015 13:36:51 -0800 Subject: [PATCH 103/585] spaces --- 247 Strobogrammatic Number II.py | 1 + 248 Strobogrammatic Number III.py | 2 +- 252 Meeting Rooms.py | 5 ++--- 255 Verify Preorder Sequence in Binary Search Tree.py | 1 + 264 Ugly Number II.py | 1 + 270 Closest Binary Search Tree Value.py | 1 + 273 Integer to English Words.py | 1 - 274 H-Index.py | 1 + 276 Paint Fence.py | 1 + 279 Perfect Squares.py | 1 + 281 Zigzag Iterator.py | 1 + 287 Find the Duplicate Number.py | 1 + 288 Unique Word Abbreviation.py | 1 - 292 Nim Game.py | 1 + 296 Best Meeting Point.py | 1 + 302 Smallest Rectangle Enclosing Black Pixels.py | 1 + 16 files changed, 15 insertions(+), 6 deletions(-) diff --git a/247 Strobogrammatic Number II.py b/247 Strobogrammatic Number II.py index 0070e82..4651f11 100644 --- a/247 Strobogrammatic Number II.py +++ b/247 Strobogrammatic Number II.py @@ -120,5 +120,6 @@ def build(self, idx, n, cur, ret): self.build(idx+1, n, cur, ret) cur.pop() + if __name__ == "__main__": assert Solution().findStrobogrammatic(3) == ['101', '609', '808', '906', '111', '619', '818', '916', '181', '689', '888', '986'] \ No newline at end of file diff --git a/248 Strobogrammatic Number III.py b/248 Strobogrammatic Number III.py index 3d5a4ca..d18e1c9 100644 --- a/248 Strobogrammatic Number III.py +++ b/248 Strobogrammatic Number III.py @@ -50,4 +50,4 @@ def build(self, n, cur, ret): cur.append(elt[1]) self.build(n, cur, ret) cur.pop() - cur.popleft() + cur.popleft() \ No newline at end of file diff --git a/252 Meeting Rooms.py b/252 Meeting Rooms.py index c3aef95..f29d8e8 100644 --- a/252 Meeting Rooms.py +++ b/252 Meeting Rooms.py @@ -1,6 +1,8 @@ """ Premium Question """ +import operator + __author__ = 'Daniel' @@ -10,9 +12,6 @@ def __init__(self, s=0, e=0): self.end = e -import operator - - class Solution: def canAttendMeetings(self, intervals): """ diff --git a/255 Verify Preorder Sequence in Binary Search Tree.py b/255 Verify Preorder Sequence in Binary Search Tree.py index 724a650..54199ae 100644 --- a/255 Verify Preorder Sequence in Binary Search Tree.py +++ b/255 Verify Preorder Sequence in Binary Search Tree.py @@ -26,6 +26,7 @@ def verifyPreorder(self, preorder): return True + if __name__ == "__main__": preorder = [3, 5, 2, 1, 4, 7, 6, 9, 8, 10] assert Solution().verifyPreorder(preorder) == False diff --git a/264 Ugly Number II.py b/264 Ugly Number II.py index ea37cb8..ff1c3b0 100644 --- a/264 Ugly Number II.py +++ b/264 Ugly Number II.py @@ -56,5 +56,6 @@ def nthUglyNumber(self, n): return ret + if __name__ == "__main__": assert Solution().nthUglyNumber(10) == 12 diff --git a/270 Closest Binary Search Tree Value.py b/270 Closest Binary Search Tree Value.py index b4b1c9a..d8c0e05 100644 --- a/270 Closest Binary Search Tree Value.py +++ b/270 Closest Binary Search Tree Value.py @@ -51,5 +51,6 @@ def find(self, root, target, ret, lower=True): self.find(root.left, target, ret, lower) + if __name__ == "__main__": assert Solution().closestValue(TreeNode(2147483647), 0.0) == 2147483647 diff --git a/273 Integer to English Words.py b/273 Integer to English Words.py index a0ba49d..8432731 100644 --- a/273 Integer to English Words.py +++ b/273 Integer to English Words.py @@ -43,7 +43,6 @@ def __init__(self): 1000: "Thousand", 1000000: "Million", 1000000000: "Billion" - } def numberToWords(self, num): diff --git a/274 H-Index.py b/274 H-Index.py index c868470..0f2743c 100644 --- a/274 H-Index.py +++ b/274 H-Index.py @@ -73,5 +73,6 @@ def hIndex_reverse_sort(self, citations): return h + if __name__ == "__main__": assert Solution().hIndex([3, 0, 6, 1, 5]) == 3 \ No newline at end of file diff --git a/276 Paint Fence.py b/276 Paint Fence.py index 790587d..e47f94b 100644 --- a/276 Paint Fence.py +++ b/276 Paint Fence.py @@ -110,6 +110,7 @@ def numWays_MLE(self, n, k): return ret + if __name__ == "__main__": assert Solution().numWays(3, 2) == 6 diff --git a/279 Perfect Squares.py b/279 Perfect Squares.py index 1ffbca9..bb2b42f 100644 --- a/279 Perfect Squares.py +++ b/279 Perfect Squares.py @@ -12,6 +12,7 @@ class Solution(object): F = [0] # static dp for all test cases + def numSquares(self, n): """ static dp diff --git a/281 Zigzag Iterator.py b/281 Zigzag Iterator.py index 0f23bb5..4f0fccb 100644 --- a/281 Zigzag Iterator.py +++ b/281 Zigzag Iterator.py @@ -47,6 +47,7 @@ def hasNext(self): """ return self.j <= self.maxa[0] + if __name__ == "__main__": v1 = [1, 2] v2 = [3, 4, 5, 6] diff --git a/287 Find the Duplicate Number.py b/287 Find the Duplicate Number.py index 4d9e1a6..28124ac 100644 --- a/287 Find the Duplicate Number.py +++ b/287 Find the Duplicate Number.py @@ -41,6 +41,7 @@ def findDuplicate(self, nums): return t + if __name__ == "__main__": assert Solution().findDuplicate([1, 2, 3 ,4, 5, 5]) == 5 diff --git a/288 Unique Word Abbreviation.py b/288 Unique Word Abbreviation.py index f7637e8..7742265 100644 --- a/288 Unique Word Abbreviation.py +++ b/288 Unique Word Abbreviation.py @@ -3,7 +3,6 @@ """ from collections import defaultdict - __author__ = 'Daniel' diff --git a/292 Nim Game.py b/292 Nim Game.py index 8443b68..4444651 100644 --- a/292 Nim Game.py +++ b/292 Nim Game.py @@ -55,5 +55,6 @@ def canWinNim_MLE(self, n): return F[n] + if __name__ == "__main__": assert Solution().canWinNim(5) \ No newline at end of file diff --git a/296 Best Meeting Point.py b/296 Best Meeting Point.py index c749745..b466547 100644 --- a/296 Best Meeting Point.py +++ b/296 Best Meeting Point.py @@ -40,5 +40,6 @@ def minTotalDistance(self, grid): return ret + if __name__ == "__main__": assert Solution().minTotalDistance([[1,0,0,0,1],[0,0,0,0,0],[0,0,1,0,0]]) == 6 \ No newline at end of file diff --git a/302 Smallest Rectangle Enclosing Black Pixels.py b/302 Smallest Rectangle Enclosing Black Pixels.py index 14fe263..a6e8879 100644 --- a/302 Smallest Rectangle Enclosing Black Pixels.py +++ b/302 Smallest Rectangle Enclosing Black Pixels.py @@ -30,6 +30,7 @@ def minArea(self, image, x, y): x_hi = bisect.bisect_left(map(lambda e: 1^e, xaxis), 1, x) return (y_hi-y_lo)*(x_hi-x_lo) + if __name__ == "__main__": image = [ "00", From cadf6880dedfa366e649643bfb93cbfbe8d2294a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 11 Dec 2015 21:47:30 -0800 Subject: [PATCH 104/585] stack --- 316 Remove Duplicate Letters.py | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 316 Remove Duplicate Letters.py diff --git a/316 Remove Duplicate Letters.py b/316 Remove Duplicate Letters.py new file mode 100644 index 0000000..e8d6907 --- /dev/null +++ b/316 Remove Duplicate Letters.py @@ -0,0 +1,45 @@ +""" +Given a string which contains only lowercase letters, remove duplicate letters so that every letter appear once and only +once. You must make sure your result is the smallest in lexicographical order among all possible results. + +Example: +Given "bcabc" +Return "abc" + +Given "cbacdcbc" +Return "acdb" +""" +__author__ = 'Daniel' + + +class Solution(object): + def removeDuplicateLetters(self, s): + """ + :type s: str + :rtype: str + """ + last_pos = [-1 for _ in xrange(26)] + n = len(s) + for i in xrange(n-1, -1, -1): + if last_pos[self._idx(s[i])] == -1: + last_pos[self._idx(s[i])] = i + + stk = [] + stk_set = set() + for i in xrange(n): + v = s[i] + if v not in stk_set: + while stk and stk[-1] > v and last_pos[self._idx(stk[-1])] > i: + p = stk.pop() + stk_set.remove(p) + stk.append(v) + stk_set.add(v) + + return "".join(stk) + + def _idx(self, x): + return ord(x) - ord('a') + + +if __name__ == "__main__": + assert Solution().removeDuplicateLetters("cbacdcbc") == "acdb" \ No newline at end of file From 28c935e7291a18ce5dbf163e1b53f86662e7f8a0 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 11 Dec 2015 22:29:53 -0800 Subject: [PATCH 105/585] column traversal --- 314 Binary Tree Vertical Order Traversal.py | 49 +++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 314 Binary Tree Vertical Order Traversal.py diff --git a/314 Binary Tree Vertical Order Traversal.py b/314 Binary Tree Vertical Order Traversal.py new file mode 100644 index 0000000..d91f4c3 --- /dev/null +++ b/314 Binary Tree Vertical Order Traversal.py @@ -0,0 +1,49 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def verticalOrder(self, root): + """ + O(N) + :type root: TreeNode + :rtype: List[List[int]] + """ + l = self.leftmost(root, 0) + r = self.rightmost(root, 0) + + ret = [[] for _ in xrange(r-l-1)] + self.bfs(root, -l-1, ret) + return ret + + def bfs(self, cur, col, ret): + q = [] + if cur: + q.append((cur, col)) + + while q: + l = len(q) + for i in xrange(l): # avoid non-stop access as in `for elt in q` + v, c = q[i] + ret[c].append(v.val) + if v.left: q.append((v.left, c-1)) + if v.right: q.append((v.right, c+1)) + + q = q[l:] + + def leftmost(self, cur, l): + if not cur: return l + return min(self.leftmost(cur.left, l-1), self.leftmost(cur.right, l+1)) + + def rightmost(self, cur, r): + if not cur: return r + return max(self.rightmost(cur.left, r-1), self.rightmost(cur.right, r+1)) From 8b69dd2a181f1d647f613e037ce1298764c9b0b4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 12 Dec 2015 12:29:00 -0800 Subject: [PATCH 106/585] dp solution --- 313 Super Ugly Number.py | 77 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 313 Super Ugly Number.py diff --git a/313 Super Ugly Number.py b/313 Super Ugly Number.py new file mode 100644 index 0000000..f993427 --- /dev/null +++ b/313 Super Ugly Number.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +""" +Write a program to find the nth super ugly number. + +Super ugly numbers are positive numbers whose all prime factors are in the given prime list primes of size k. For +example, [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] is the sequence of the first 12 super ugly numbers given primes = +[2, 7, 13, 19] of size 4. + +Note: +(1) 1 is a super ugly number for any given primes. +(2) The given numbers in primes are in ascending order. +(3) 0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000. +""" +import heapq +from collections import deque +import sys + +__author__ = 'Daniel' + + +class Solution(object): + def nthSuperUglyNumber(self, n, primes): + """ + DP O(kn) + :type n: int + :type primes: List[int] + :rtype: int + """ + k = len(primes) + ret = [sys.maxint for _ in xrange(n)] + ret[0] = 1 + # for each prime, a pointer pointing to the value of next unused number in the result + idxes = [0 for _ in xrange(k)] + for i in xrange(1, n): + for j in xrange(k): + ret[i] = min(ret[i], primes[j]*ret[idxes[j]]) + + for j in xrange(k): + if ret[i] == primes[j]*ret[idxes[j]]: + idxes[j] += 1 + + return ret[n-1] + + +class QueueWrapper(object): + def __init__(self, idx, q): + self.idx = idx + self.q = q + + def __cmp__(self, other): + return self.q[0] - other.q[0] + + +class SolutionHeap(object): + def nthSuperUglyNumber(self, n, primes): + """ + O(k lg k) + O(nk) + :type n: int + :type primes: List[int] + :rtype: int + """ + ret = 1 + h = [QueueWrapper(i, deque([v])) for i, v in enumerate(primes)] + dic = {e.idx: e for e in h} + + heapq.heapify(h) + for _ in xrange(n-1): + mini = heapq.heappop(h) + ret = mini.q.popleft() + for i in xrange(mini.idx, len(primes)): + dic[i].q.append(ret*primes[i]) + heapq.heappush(h, mini) + + return ret + +if __name__ == "__main__": + assert Solution().nthSuperUglyNumber(12, [2, 7, 13, 19]) == 32 \ No newline at end of file From 7fe4bce5b2f3cc13b6c52ffbff60c158f2271818 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 12 Dec 2015 14:28:54 -0800 Subject: [PATCH 107/585] Diameter of a tree --- 310 Minimum Height Trees.py | 195 ++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 310 Minimum Height Trees.py diff --git a/310 Minimum Height Trees.py b/310 Minimum Height Trees.py new file mode 100644 index 0000000..af29b8f --- /dev/null +++ b/310 Minimum Height Trees.py @@ -0,0 +1,195 @@ +""" +For a undirected graph with tree characteristics, we can choose any node as the root. The result graph is then a rooted +tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs). Given such a +graph, write a function to find all the MHTs and return a list of their root labels. + +Format +The graph contains n nodes which are labeled from 0 to n - 1. You will be given the number n and a list of undirected +edges (each edge is a pair of labels). + +You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, +0] and thus will not appear together in edges. + +Example 1: + +Given n = 4, edges = [[1, 0], [1, 2], [1, 3]] + + 0 + | + 1 + / \ + 2 3 +return [1] + +Example 2: + +Given n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]] + + 0 1 2 + \ | / + 3 + | + 4 + | + 5 +return [3, 4] +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class Solution(object): + def findMinHeightTrees(self, n, edges): + """ + Longest path algorithm + Diameter of a tree + :type n: int + :type edges: List[List[int]] + :rtype: List[int] + """ + if not edges: + return [0] + + V = {i: [] for i in xrange(n)} + for a, b in edges: + V[a].append(b) + V[b].append(a) + + # longest path algorithm + _, _, last = self.bfs(0, V) + level, pi, last = self.bfs(last, V) + + ret = [] + cur = last + for _ in xrange((level-1)/2): + cur = pi[cur] + ret.append(cur) + + if level % 2 == 0: + ret.append(pi[cur]) + + return ret + + def bfs(self, s, V): + # bfs + visisted = [False for _ in xrange(len(V))] + pi = [-1 for _ in xrange(len(V))] + last = s + level = 0 + q = [] + q.append(s) + while q: + l = len(q) + for i in xrange(l): + cur = q[i] + last = cur + visisted[cur] = True + for nbr in V[cur]: + if not visisted[nbr]: + pi[nbr] = cur + q.append(nbr) + + q = q[l:] + level += 1 + + return level, pi, last + + +class Solution_TLE(object): + def findMinHeightTrees_TLE(self, n, edges): + """ + :type n: int + :type edges: List[List[int]] + :rtype: List[int] + """ + if not edges: + return 0 + + V = {i: [] for i in xrange(n)} + for a, b in edges: + V[a].append(b) + V[b].append(a) + + ret = [] + mini = n + for k in V.keys(): + l = self.bfs(k, V) + if l < mini: + ret = [k] + mini = l + elif l == mini: + ret.append(k) + + return ret + + def bfs(self, s, V): + # bfs + visisted = [False for _ in xrange(len(V))] + q = [] + level = 0 + q.append(s) + while q: + l = len(q) + for i in xrange(l): + cur = q[i] + visisted[cur] = True + for nbr in V[cur]: + if not visisted[nbr]: + q.append(nbr) + + q = q[l:] + level += 1 + + return level + + +class SolutionError(object): + def findMinHeightTrees(self, n, edges): + """ + One pass + :type n: int + :type edges: List[List[int]] + :rtype: List[int] + """ + if not edges: + return 0 + + V = {i: [] for i in xrange(n)} + for a, b in edges: + V[a].append(b) + V[b].append(a) + + leaf = None + for k, v in V.items(): + if len(v) == 1: + leaf = k + break + + # bfs + visisted = [False for _ in xrange(n)] + h2v = defaultdict(list) + q = [] + level = 0 + q.append(leaf) + while q: + l = len(q) + for i in xrange(l): + cur = q[i] + h2v[level].append(cur) + visisted[cur] = True + for nbr in V[cur]: + if not visisted[nbr]: + q.append(nbr) + + q = q[l:] + level += 1 + + if level % 2 == 0: + return h2v[level/2-1]+h2v[level/2] + else: + return h2v[level/2] + +if __name__ == "__main__": + # print Solution().findMinHeightTrees(6, [[3,0],[3,1],[3,2],[3,4],[5,4]]) + print Solution().findMinHeightTrees(7, [[0,1],[1,2],[1,3],[2,4],[3,5],[4,6]]) \ No newline at end of file From 5d6eab69ef8a8f734d9ef7d39c6fd3951c5b90a8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 12 Dec 2015 14:44:43 -0800 Subject: [PATCH 108/585] posting list --- 311 Sparse Matrix Multiplication.py | 74 +++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 311 Sparse Matrix Multiplication.py diff --git a/311 Sparse Matrix Multiplication.py b/311 Sparse Matrix Multiplication.py new file mode 100644 index 0000000..4a9aa40 --- /dev/null +++ b/311 Sparse Matrix Multiplication.py @@ -0,0 +1,74 @@ +""" +Given two sparse matrices A and B, return the result of AB. + +You may assume that A's column number is equal to B's row number. + +Example: + +A = [ + [ 1, 0, 0], + [-1, 0, 3] +] + +B = [ + [ 7, 0, 0 ], + [ 0, 0, 0 ], + [ 0, 0, 1 ] +] + + + | 1 0 0 | | 7 0 0 | | 7 0 0 | +AB = | -1 0 3 | x | 0 0 0 | = | -7 0 3 | + | 0 0 1 | +""" +__author__ = 'Daniel' + + +class Solution(object): + def multiply(self, A, B): + """ + Brute force O(n^3) + O(n^2 k) + Posting list + :type A: List[List[int]] + :type B: List[List[int]] + :rtype: List[List[int]] + """ + m, n = len(A), len(A[0]) + A1 = [{} for _ in xrange(m)] + for i in xrange(m): + for j in xrange(n): + if A[i][j] != 0: + A1[i][j] = A[i][j] + + m, n = len(B), len(B[0]) + B1 = [{} for _ in xrange(n)] + for i in xrange(m): + for j in xrange(n): + if B[i][j] != 0: + B1[j][i] = B[i][j] + + ret = [[0 for _ in xrange(len(B[0]))] for _ in xrange(len(A))] + for i, row in enumerate(A1): + for j, col in enumerate(B1): + s = 0 + for k in row.keys(): + if k in col: + s += row[k]*col[k] + ret[i][j] = s + + return ret + +if __name__ == "__main__": + A = [ + [1, 0, 0], + [-1, 0, 3] + ] + + B = [ + [7, 0, 0], + [0, 0, 0], + [0, 0, 1] + ] + assert Solution().multiply(A, B) == [[7, 0, 0], [-7, 0, 3]] + From 8f5af770e224ccdb00a367e3a16d48ed0a327a08 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 12 Dec 2015 14:46:02 -0800 Subject: [PATCH 109/585] take out description --- 311 Sparse Matrix Multiplication.py | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/311 Sparse Matrix Multiplication.py b/311 Sparse Matrix Multiplication.py index 4a9aa40..0ef3ed1 100644 --- a/311 Sparse Matrix Multiplication.py +++ b/311 Sparse Matrix Multiplication.py @@ -1,25 +1,6 @@ """ -Given two sparse matrices A and B, return the result of AB. - -You may assume that A's column number is equal to B's row number. - -Example: - -A = [ - [ 1, 0, 0], - [-1, 0, 3] -] - -B = [ - [ 7, 0, 0 ], - [ 0, 0, 0 ], - [ 0, 0, 1 ] -] - - - | 1 0 0 | | 7 0 0 | | 7 0 0 | -AB = | -1 0 3 | x | 0 0 0 | = | -7 0 3 | - | 0 0 1 | +Premium Question +https://leetcode.com/problems/sparse-matrix-multiplication/ """ __author__ = 'Daniel' From 21f9669b546273aad46e2cdcb936f66919488b07 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 12 Dec 2015 17:04:08 -0800 Subject: [PATCH 110/585] divide and conquer --- 312 Burst Balloons.py | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 312 Burst Balloons.py diff --git a/312 Burst Balloons.py b/312 Burst Balloons.py new file mode 100644 index 0000000..2005399 --- /dev/null +++ b/312 Burst Balloons.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +""" +Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. You are +asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. Here +left and right are adjacent indices of i. After the burst, the left and right then becomes adjacent. + +Find the maximum coins you can collect by bursting the balloons wisely. + +Note: +(1) You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them. +(2) 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100 + +Example: + +Given [3, 1, 5, 8] + +Return 167 + + nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> [] + coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167 +""" +__author__ = 'Daniel' + + +class Solution(object): + def maxCoins(self, A): + """ + Divide & Conquer <- Divide Boundary <- Reverse Thinking + + Let F[i][j] be the max scores burst all over A[i:j] + F[i][j] = max(F[i][k] + A[i-1]*A[k]*A[j] + F[k+1][j] \forall k) where k is the last one to burst. + + Reference: https://leetcode.com/discuss/72216/share-some-analysis-and-explanations?show=72358#c72358 + :type A: List[int] + :rtype: int + """ + n = len(A) + + def get(i): + if i < 0 or i >= n: return 1 + return A[i] + + F = [[0 for _ in xrange(n+1)] for _ in xrange(n+1)] + for i in xrange(n+1, -1, -1): + for j in xrange(i+1, n+1): + F[i][j] = max( + F[i][k]+get(i-1)*get(k)*get(j)+F[k+1][j] + for k in xrange(i, j) + ) + + return max(map(max, F)) + + +if __name__ == "__main__": + assert Solution().maxCoins([3, 1, 5, 8]) == 167 + From 0e812bf67cb8b58246a99aa4a76f112307df0fa8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 12 Dec 2015 17:37:47 -0800 Subject: [PATCH 111/585] dp --- ...ime to Buy and Sell Stock with Cooldown.py | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 309 Best Time to Buy and Sell Stock with Cooldown.py diff --git a/309 Best Time to Buy and Sell Stock with Cooldown.py b/309 Best Time to Buy and Sell Stock with Cooldown.py new file mode 100644 index 0000000..2c0dcd7 --- /dev/null +++ b/309 Best Time to Buy and Sell Stock with Cooldown.py @@ -0,0 +1,57 @@ +""" +Say you have an array for which the ith element is the price of a given stock on day i. + +Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell +one share of the stock multiple times) with the following restrictions: + +You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). +After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day) +Example: + +prices = [1, 2, 3, 0, 2] +maxProfit = 3 +transactions = [buy, sell, cooldown, buy, sell] +""" +__author__ = 'Daniel' + + +class Solution(object): + def maxProfit(self, A): + """ + O(n^2) + Let F[i] be max profit from day 0 to day i, selling stock at day i + Let M[i] be max profit from day 0 to day i. + + F[i] = max( + F[i-1]+A[i]-A[i-1], // Sell the stock held for multiple days + // i.e. revert previous transaction, sell at day i instead of day (i-1) + M[i-3]+A[i]-A[i-1] // Sell the stock held for 1 days. + ) + M[i] = max(M[i-1], F[i]) + :type A: List[int] + :rtype: int + """ + n = len(A) + if n == 0 or n == 1: + return 0 + if n == 2: + return max(0, A[1]-A[0]) + + CD = 2 # cool down + F = [0 for _ in xrange(n)] + M = [0 for _ in xrange(n)] + F[1] = A[1]-A[0] + M[1] = max(F[:2]) + F[2] = max(A[2]-A[2-1-i] for i in xrange(2)) + M[2] = max(M[1], F[2]) + + # core + for i in xrange(3, n): + F[i] = max(F[i-1]+A[i]-A[i-1], M[i-2-CD]+A[i]-A[i-1]) + M[i] = max(M[i-1], F[i]) + + return M[-1] + + +if __name__ == "__main__": + assert Solution().maxProfit([1, 2, 3, 0, 2]) == 3 \ No newline at end of file From 7e922d5c0d807e39f4ad5bfffe15fde20380bd39 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 12 Dec 2015 17:40:03 -0800 Subject: [PATCH 112/585] fix typo --- 309 Best Time to Buy and Sell Stock with Cooldown.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/309 Best Time to Buy and Sell Stock with Cooldown.py b/309 Best Time to Buy and Sell Stock with Cooldown.py index 2c0dcd7..229e241 100644 --- a/309 Best Time to Buy and Sell Stock with Cooldown.py +++ b/309 Best Time to Buy and Sell Stock with Cooldown.py @@ -37,11 +37,11 @@ def maxProfit(self, A): if n == 2: return max(0, A[1]-A[0]) - CD = 2 # cool down + CD = 1 # cool down F = [0 for _ in xrange(n)] M = [0 for _ in xrange(n)] F[1] = A[1]-A[0] - M[1] = max(F[:2]) + M[1] = max(M[0], F[1]) F[2] = max(A[2]-A[2-1-i] for i in xrange(2)) M[2] = max(M[1], F[2]) From 6566ca488b290734c06a3f4c897492d31f3403e0 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 12 Dec 2015 22:22:16 -0800 Subject: [PATCH 113/585] segment tree --- ...ime to Buy and Sell Stock with Cooldown.py | 2 +- 315 Count of Smaller Numbers After Self.py | 91 +++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 315 Count of Smaller Numbers After Self.py diff --git a/309 Best Time to Buy and Sell Stock with Cooldown.py b/309 Best Time to Buy and Sell Stock with Cooldown.py index 229e241..7cc696d 100644 --- a/309 Best Time to Buy and Sell Stock with Cooldown.py +++ b/309 Best Time to Buy and Sell Stock with Cooldown.py @@ -25,7 +25,7 @@ def maxProfit(self, A): F[i] = max( F[i-1]+A[i]-A[i-1], // Sell the stock held for multiple days // i.e. revert previous transaction, sell at day i instead of day (i-1) - M[i-3]+A[i]-A[i-1] // Sell the stock held for 1 days. + M[i-3]+A[i]-A[i-1] // Sell the stock held for 1 day. ) M[i] = max(M[i-1], F[i]) :type A: List[int] diff --git a/315 Count of Smaller Numbers After Self.py b/315 Count of Smaller Numbers After Self.py new file mode 100644 index 0000000..1e5224d --- /dev/null +++ b/315 Count of Smaller Numbers After Self.py @@ -0,0 +1,91 @@ +""" +You are given an integer array nums and you have to return a new counts array. The counts array has the property where +counts[i] is the number of smaller elements to the right of nums[i]. + +Example: + +Given nums = [5, 2, 6, 1] + +To the right of 5 there are 2 smaller elements (2 and 1). +To the right of 2 there is only 1 smaller element (1). +To the right of 6 there is 1 smaller element (1). +To the right of 1 there is 0 smaller element. +Return the array [2, 1, 1, 0]. +""" +__author__ = 'Daniel' + + +class TreeNode(object): + def __init__(self, start, end, cnt=0): + self.start = start + self.end = end + self.cnt = cnt + self.left = None + self.right = None + + +class SegmentTree(object): + def __init__(self, n): + self.root = self.build(0, n) + + def build(self, start, end): + if start >= end: return + if start == end-1: return TreeNode(start, end) + node = TreeNode(start, end) + node.left = self.build(start, (start+end)/2) + node.right = self.build((start+end)/2, end) + return node + + def inc(self, idx, val): + cur = self.root + while cur: + cur.cnt += val + mid = (cur.start+cur.end)/2 + if cur.start <= idx < mid: + cur = cur.left + elif mid <= idx < cur.end: + cur = cur.right + else: + return + + def query_less(self, cur, idx): + if not cur: + return 0 + + mid = (cur.start+cur.end)/2 + if cur.start <= idx < mid: + return self.query_less(cur.left, idx) + elif mid <= idx < cur.end: + return (cur.left.cnt if cur.left else 0) + self.query_less(cur.right, idx) + else: + return 0 + + +class Solution(object): + def countSmaller(self, nums): + """ + Brute force: O(n^2) + Segment Tree + O(n lg n) + :type nums: List[int] + :rtype: List[int] + """ + # preprocess the array to make it discrete in [0, 1, ..., n-1] + h = {} + for i, v in enumerate(sorted(nums)): + h[v] = i # override duplicates + + A = [h[v] for v in nums] + n = len(A) + st = SegmentTree(n) + ret = [] + for i in xrange(n-1, -1, -1): + ret.append(st.query_less(st.root, A[i])) + st.inc(A[i], 1) + + return ret[::-1] + + +if __name__ == "__main__": + assert Solution().countSmaller([5, 2, 6, 1]) == [2, 1, 1, 0] + assert Solution().countSmaller([-1, -1]) == [2, 1, 1, 0] From 1392af5f3448d84b6e6c6b34f6edc70a0608e53a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 13 Dec 2015 19:46:42 -0800 Subject: [PATCH 114/585] reduce complexity --- 297 Serialize and Deserialize Binary Tree.py | 35 ++++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/297 Serialize and Deserialize Binary Tree.py b/297 Serialize and Deserialize Binary Tree.py index 9c589e0..aa8f6a7 100644 --- a/297 Serialize and Deserialize Binary Tree.py +++ b/297 Serialize and Deserialize Binary Tree.py @@ -52,17 +52,10 @@ def serialize(self, root): l = len(q) for i in xrange(l): cur = q[i] - if cur.left: - q.append(cur.left) - ret.append(str(cur.left.val)) - else: - ret.append("null") - - if cur.right: - q.append(cur.right) - ret.append(str(cur.right.val)) - else: - ret.append("null") + if cur.left: q.append(cur.left) + ret.append(self.encode(cur.left)) + if cur.right: q.append(cur.right) + ret.append(self.encode(cur.right)) q = q[l:] @@ -77,19 +70,19 @@ def deserialize(self, data): """ lst = data.split(",") root = self.decode(lst[0]) + q = deque([root]) i = 1 - while i < len(lst): + while q: cur = q.popleft() - cur.left = self.decode(lst[i]) - i += 1 - if cur.left: - q.append(cur.left) + if i < len(lst): + cur.left = self.decode(lst[i]) + i += 1 + if cur.left: q.append(cur.left) if i < len(lst): cur.right = self.decode(lst[i]) i += 1 - if cur.right: - q.append(cur.right) + if cur.right: q.append(cur.right) return root @@ -98,3 +91,9 @@ def decode(self, s): return None else: return TreeNode(int(s)) + + def encode(self, node): + if not node: + return "null" + else: + return str(node.val) From ce703f9fa0edfd73303beb49b533e61f1246f661 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 13 Dec 2015 23:33:20 -0800 Subject: [PATCH 115/585] doc --- 122 Best Time to Buy and Sell Stock.py | 19 ++++++---------- 270 Closest Binary Search Tree Value.py | 22 ++++++++----------- 297 Serialize and Deserialize Binary Tree.py | 2 +- ...inary Tree Longest Consecutive Sequence.py | 19 ++++++++-------- 314 Binary Tree Vertical Order Traversal.py | 4 ++++ 315 Count of Smaller Numbers After Self.py | 2 +- 6 files changed, 32 insertions(+), 36 deletions(-) diff --git a/122 Best Time to Buy and Sell Stock.py b/122 Best Time to Buy and Sell Stock.py index a67f147..7f8fe2f 100644 --- a/122 Best Time to Buy and Sell Stock.py +++ b/122 Best Time to Buy and Sell Stock.py @@ -8,30 +8,25 @@ class Solution(object): - def maxProfit(self, prices): + def maxProfit(self, A): """ Maximum subarray sum DP version Let F[i] be the maximum subarray sum ending at A[i-1] """ - if len(prices) <= 1: + if len(A) <= 1: return 0 - delta_prices = [] - for i in xrange(1, len(prices)): - delta_prices.append(prices[i]-prices[i-1]) - - A = delta_prices n = len(A) F = [0 for _ in xrange(n+1)] - maxa = A[0] - for i in xrange(1, n+1): - F[i] = max(F[i-1]+A[i-1], 0) + maxa = 0 + for i in xrange(2, n+1): + F[i] = max(F[i-1] + A[i-1] - A[i-2], 0) # revert the previous transaction maxa = max(maxa, F[i]) return maxa - def maxProfit(self, prices): + def maxProfitDelta(self, prices): """ Only long position allowed, cannot short @@ -61,4 +56,4 @@ def maxProfit(self, prices): if __name__ == "__main__": - print Solution().maxProfit([3, 2, 1, 4, 5, 6, 2]) \ No newline at end of file + assert Solution().maxProfit([3, 2, 1, 4, 5, 6, 2]) == 5 \ No newline at end of file diff --git a/270 Closest Binary Search Tree Value.py b/270 Closest Binary Search Tree Value.py index d8c0e05..11f92f0 100644 --- a/270 Closest Binary Search Tree Value.py +++ b/270 Closest Binary Search Tree Value.py @@ -23,14 +23,14 @@ def closestValue(self, root, target): :type target: float :rtype: int """ - lower = [-sys.float_info.max] - self.find(root, target, lower, True) - higher = [sys.float_info.max] - self.find(root, target, higher, False) - if higher[0] - target < target - lower[0]: - return int(higher[0]) + lo = [-sys.float_info.max] + self.find(root, target, lo, True) + hi = [sys.float_info.max] + self.find(root, target, hi, False) + if hi[0] - target < target - lo[0]: + return int(hi[0]) else: - return int(lower[0]) + return int(lo[0]) def find(self, root, target, ret, lower=True): if not root: @@ -41,14 +41,10 @@ def find(self, root, target, ret, lower=True): return if root.val < target: - if lower: - ret[0] = max(ret[0], root.val) - + if lower: ret[0] = max(ret[0], root.val) self.find(root.right, target, ret, lower) else: - if not lower: - ret[0] = min(ret[0], root.val) - + if not lower: ret[0] = min(ret[0], root.val) self.find(root.left, target, ret, lower) diff --git a/297 Serialize and Deserialize Binary Tree.py b/297 Serialize and Deserialize Binary Tree.py index aa8f6a7..622bb70 100644 --- a/297 Serialize and Deserialize Binary Tree.py +++ b/297 Serialize and Deserialize Binary Tree.py @@ -70,7 +70,7 @@ def deserialize(self, data): """ lst = data.split(",") root = self.decode(lst[0]) - + q = deque([root]) i = 1 while q: diff --git a/298 Binary Tree Longest Consecutive Sequence.py b/298 Binary Tree Longest Consecutive Sequence.py index 6622200..3738fab 100644 --- a/298 Binary Tree Longest Consecutive Sequence.py +++ b/298 Binary Tree Longest Consecutive Sequence.py @@ -15,28 +15,29 @@ def __init__(self, x): class Solution(object): def __init__(self): - self.maxa = 0 + self.gmax = 0 def longestConsecutive(self, root): self.longest(root) - return self.maxa + return self.gmax - def longest(self, root): + def longest(self, cur): """ longest ended at root + Only consider increasing order """ - if not root: + if not cur: return 0 maxa = 1 - l = self.longest(root.left) - r = self.longest(root.right) - if root.left and root.val+1 == root.left.val: + l = self.longest(cur.left) + r = self.longest(cur.right) + if cur.left and cur.val+1 == cur.left.val: maxa = max(maxa, l+1) - if root.right and root.val+1 == root.right.val: + if cur.right and cur.val+1 == cur.right.val: maxa = max(maxa, r+1) - self.maxa = max(self.maxa, maxa) + self.gmax = max(self.gmax, maxa) return maxa def longestConsecutive_error(self, root): diff --git a/314 Binary Tree Vertical Order Traversal.py b/314 Binary Tree Vertical Order Traversal.py index d91f4c3..146c231 100644 --- a/314 Binary Tree Vertical Order Traversal.py +++ b/314 Binary Tree Vertical Order Traversal.py @@ -47,3 +47,7 @@ def leftmost(self, cur, l): def rightmost(self, cur, r): if not cur: return r return max(self.rightmost(cur.left, r-1), self.rightmost(cur.right, r+1)) + + def sidemost(self, cur, p, f): + if not cur: return p + return f(self.sidemost(cur.left, p-1, f), self.sidemost(cur.right, p+1, f)) \ No newline at end of file diff --git a/315 Count of Smaller Numbers After Self.py b/315 Count of Smaller Numbers After Self.py index 1e5224d..539d4e4 100644 --- a/315 Count of Smaller Numbers After Self.py +++ b/315 Count of Smaller Numbers After Self.py @@ -88,4 +88,4 @@ def countSmaller(self, nums): if __name__ == "__main__": assert Solution().countSmaller([5, 2, 6, 1]) == [2, 1, 1, 0] - assert Solution().countSmaller([-1, -1]) == [2, 1, 1, 0] + assert Solution().countSmaller([-1, -1]) == [0, 0] From 3a2efca0de8a5db03f37eb0a56d7db6fbce6c976 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 13 Dec 2015 23:39:02 -0800 Subject: [PATCH 116/585] bfs --- 317 Shortest Distance from All Buildings.py | 75 +++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 317 Shortest Distance from All Buildings.py diff --git a/317 Shortest Distance from All Buildings.py b/317 Shortest Distance from All Buildings.py new file mode 100644 index 0000000..1f73e44 --- /dev/null +++ b/317 Shortest Distance from All Buildings.py @@ -0,0 +1,75 @@ +""" +Premium Question +""" +import sys + +__author__ = 'Daniel' + + +class Solution(object): + def __init__(self): + self.dirs = [(-1, 0), (1, 0), (0, -1), (0, 1)] + + def shortestDistance(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + m = len(grid) + n = len(grid[0]) + acc = [[0 for _ in xrange(n)] for _ in xrange(m)] + reachable = [[True for _ in xrange(n)] for _ in xrange(m)] + for i in xrange(m): + for j in xrange(n): + if grid[i][j] > 0: + reachable[i][j] = False + acc[i][j] = sys.maxint + + for i in xrange(m): + for j in xrange(n): + if grid[i][j] == 1: + self.bfs(grid, acc, reachable, i, j) + + mini = sys.maxint + for i in xrange(m): + for j in xrange(n): + if acc[i][j] < mini and reachable[i][j]: + mini = acc[i][j] + + return mini if mini != sys.maxint else -1 + + def bfs(self, grid, acc, reachable, x, y): + d = 0 + m, n = len(grid), len(grid[0]) + visited = [[False for _ in xrange(n)] for _ in xrange(m)] + + q = [(x, y)] + visited[x][y] = True # enqueue, then visited + while q: + l = len(q) + for idx in xrange(l): + i, j = q[idx] + acc[i][j] += d + + for dir in self.dirs: + I = i+dir[0] + J = j+dir[1] + if 0 <= I < m and 0 <= J < n and grid[I][J] == 0 and not visited[I][J]: + q.append((I, J)) + visited[I][J] = True + + d += 1 + q = q[l:] + + for i in xrange(m): + for j in xrange(n): + if not visited[i][j]: + reachable[i][j] = False + + +if __name__ == "__main__": + assert Solution().shortestDistance( + [[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]]) + assert Solution().shortestDistance([[1, 0, 2, 0, 1], [0, 0, 0, 0, 0], [0, 0, 1, 0, 0]]) \ No newline at end of file From 792ddac2e6c4bd4a681676f78ffd9458aacbb465 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 13 Dec 2015 23:40:33 -0800 Subject: [PATCH 117/585] fix test case --- 317 Shortest Distance from All Buildings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/317 Shortest Distance from All Buildings.py b/317 Shortest Distance from All Buildings.py index 1f73e44..8a22d72 100644 --- a/317 Shortest Distance from All Buildings.py +++ b/317 Shortest Distance from All Buildings.py @@ -71,5 +71,5 @@ def bfs(self, grid, acc, reachable, x, y): assert Solution().shortestDistance( [[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]]) - assert Solution().shortestDistance([[1, 0, 2, 0, 1], [0, 0, 0, 0, 0], [0, 0, 1, 0, 0]]) \ No newline at end of file + 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 From 61158a76ee34a896c1073b3bcb3b50a7aa910cff Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 14 Dec 2015 23:10:26 -0800 Subject: [PATCH 118/585] line style --- 001 Two Sum.py | 1 + 224 Basic Calculator.py | 1 + 2 files changed, 2 insertions(+) diff --git a/001 Two Sum.py b/001 Two Sum.py index cc13d16..88e5548 100644 --- a/001 Two Sum.py +++ b/001 Two Sum.py @@ -52,5 +52,6 @@ def twoSum(self, num, target): if ind1!=ind2: return ind1+1, ind2+1 + if __name__=="__main__": print Solution().twoSum([3, 2, 4], 6) \ No newline at end of file diff --git a/224 Basic Calculator.py b/224 Basic Calculator.py index 31303e0..3169fe4 100644 --- a/224 Basic Calculator.py +++ b/224 Basic Calculator.py @@ -91,6 +91,7 @@ def eval_postfix(self, post): assert len(stk) == 1 return int(stk[-1]) + if __name__ == "__main__": assert Solution().calculate(" 2-1 + 2 ") == 3 assert Solution().calculate("(1+(4+5+2)-3)+(6+8)") == 23 \ No newline at end of file From 15d36cee59035cdcf19b47b8274894daf59a8a05 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 17 Dec 2015 11:50:30 -0800 Subject: [PATCH 119/585] bit man --- 318 Maximum Product of Word Lengths.py | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 318 Maximum Product of Word Lengths.py diff --git a/318 Maximum Product of Word Lengths.py b/318 Maximum Product of Word Lengths.py new file mode 100644 index 0000000..dda2e88 --- /dev/null +++ b/318 Maximum Product of Word Lengths.py @@ -0,0 +1,50 @@ +""" +Given a string array words, find the maximum value of length(word[i]) * length(word[j]) where the two words do not share +common letters. You may assume that each word will contain only lower case letters. If no such two words exist, return +0. + +Example 1: +Given ["abcw", "baz", "foo", "bar", "xtfn", "abcdef"] +Return 16 +The two words can be "abcw", "xtfn". + +Example 2: +Given ["a", "ab", "abc", "d", "cd", "bcd", "abcd"] +Return 4 +The two words can be "ab", "cd". + +Example 3: +Given ["a", "aa", "aaa", "aaaa"] +Return 0 +No such pair of words. +""" +__author__ = 'Daniel' + + +class Solution(object): + def maxProduct(self, words): + """ + Brute force: O(n*n*k) + Encode string using bit manipulation + :type words: List[str] + :rtype: int + """ + l = map(len, words) + codes = map(self.encode, words) + maxa = 0 + for i in xrange(len(codes)): + for j in xrange(i+1, len(codes)): + if codes[i] & codes[j] == 0: + maxa = max(maxa, l[i]*l[j]) + + return maxa + + def encode(self, x): + ret = 0 + for c in x: + ret |= 1 << (ord(c)-ord('a')) + return ret + + +if __name__ == "__main__": + assert Solution().maxProduct(["abcw", "baz", "foo", "bar", "xtfn", "abcdef"]) == 16 \ No newline at end of file From cc89e8699eb595b058fb0d235227ed2ff6d979ce Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 17 Dec 2015 12:19:03 -0800 Subject: [PATCH 120/585] dfs --- 301 Remove Invalid Parentheses.py | 64 ++++++++++++++++++------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/301 Remove Invalid Parentheses.py b/301 Remove Invalid Parentheses.py index c81b88b..cd833c6 100644 --- a/301 Remove Invalid Parentheses.py +++ b/301 Remove Invalid Parentheses.py @@ -15,12 +15,21 @@ class Solution(object): def removeInvalidParentheses(self, s): """ + Algorithm focuses on left parentheses + Backtracking/DFS with pruning :type s: str :rtype: List[str] """ - n = len(s) - # solve min count of removal - cnt = 0 + rmcnt = self.minrm(s) + ret = [] + self.dfs(s, "", 0, None, 0, rmcnt, ret) + return ret + + def minrm(self, s): + """ + returns minimal number of removals + """ + rmcnt = 0 l = 0 for c in s: if c == "(": @@ -29,38 +38,39 @@ def removeInvalidParentheses(self, s): l -= 1 if l < 0: l = 0 - cnt += 1 + rmcnt += 1 - cnt += l - ret = [] - self.dfs(s, "", 0, None, 0, cnt, ret) - return ret + rmcnt += l + return rmcnt - def dfs(self, s, cur, l, removed, i, cnt, ret): - """backtracking, post-check""" - if l < 0 or cnt < 0 or i > len(s): + def dfs(self, s, cur, l, last_rm, i, rmcnt, ret): + """ + Remove parenthesis + backtracking, post-check + :param s: original string + :param cur: current string builder + :param l: number of remaining left parentheses in s[0..i] + :param last_rm: last removed char + :param rmcnt: number of remaining removals needed + :param ret: results + """ + if l < 0 or rmcnt < 0 or i > len(s): return if i == len(s): - if cnt == 0 and l == 0: + if rmcnt == 0 and l == 0: ret.append(cur) return - if s[i] in ("(", ")"): - # jump - C = cnt - while i < len(s) and removed and removed == s[i]: - i += 1 - C -= 1 - - if C != cnt: - self.dfs(s, cur, l, removed, i, C, ret) - else: - self.dfs(s, cur, l, s[i], i+1, cnt-1, ret) - - L = l+1 if s[i] == "(" else l-1 - self.dfs(s, cur+s[i], L, None, i+1, cnt, ret) # put + if s[i] not in ("(", ")"): # skip non-parenthesis + self.dfs(s, cur+s[i], l, None, i+1, rmcnt, ret) else: - self.dfs(s, cur+s[i], l, None, i+1, cnt, ret) + if last_rm == s[i]: # jump, if rm, rm them all to avoid duplication + while i < len(s) and last_rm and last_rm == s[i]: i, rmcnt = i+1, rmcnt-1 + self.dfs(s, cur, l, last_rm, i, rmcnt, ret) + else: + self.dfs(s, cur, l, s[i], i+1, rmcnt-1, ret) + L = l+1 if s[i] == "(" else l-1 # consume "(" + self.dfs(s, cur+s[i], L, None, i+1, rmcnt, ret) # put if __name__ == "__main__": From a32b3429f8bfdfe5de1fd8b9e21b72608a8b5890 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 17 Dec 2015 12:39:19 -0800 Subject: [PATCH 121/585] h-index --- 274 H-Index.py | 33 +++++++++++++++++---------------- 275 H-Index II.py | 8 ++++---- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/274 H-Index.py b/274 H-Index.py index 0f2743c..0f9d298 100644 --- a/274 H-Index.py +++ b/274 H-Index.py @@ -15,28 +15,29 @@ class Solution(object): - def hIndex(self, citations): + def hIndex(self, A): """ + Determine the range of output (i.e. h-index): + Range of output: [0, N] + Chunk by N Reverse mapping & DP - Determine the range of h-index - Chunk by n - Let F[i] be the #paper with i citations (later transform F[i] to #paoer with >= i citations - :type citations: List[int] + Let cnt[i] be the #paper with == i citations + Let F[i] be the #paper with >= i citations + F[i] = F[i+1] + cnt[i] + :type A: List[int] :rtype: int """ - n = len(citations) - F = [0 for _ in xrange(n+1)] - for elt in citations: - if elt >= n: # chunk - F[n] += 1 + n = len(A) + cnt = [0 for _ in xrange(n+1)] + for e in A: + if e >= n: # chunk + cnt[n] += 1 else: - F[elt] += 1 - - if F[n] >= n: - return n + cnt[e] += 1 - for i in xrange(n-1, -1, -1): - F[i] += F[i+1] + F = [0 for _ in xrange(n+2)] + for i in xrange(n, -1, -1): + F[i] += F[i+1] + cnt[i] if F[i] >= i: return i diff --git a/275 H-Index II.py b/275 H-Index II.py index 2d44f12..bd890c0 100644 --- a/275 H-Index II.py +++ b/275 H-Index II.py @@ -5,19 +5,19 @@ class Solution(object): - def hIndex(self, citations): + def hIndex(self, A): """ Given sorted -> binary search From linear search into bin search - :type citations: List[int] + :type A: List[int] :rtype: int """ - n = len(citations) + n = len(A) s = 0 e = n while s < e: m = (s+e)/2 - if citations[m] >= n-m: + if A[m] >= n-m: e = m else: s = m+1 From 4fae00892fd5b968b419a52ffd25c5741ce9179e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 17 Dec 2015 12:51:29 -0800 Subject: [PATCH 122/585] integer to enligsh word parsing --- 273 Integer to English Words.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/273 Integer to English Words.py b/273 Integer to English Words.py index 8432731..ea1f66c 100644 --- a/273 Integer to English Words.py +++ b/273 Integer to English Words.py @@ -12,6 +12,7 @@ class Solution(object): def __init__(self): self.m = { + 0: None, 1: "One", 2: "Two", 3: "Three", @@ -47,6 +48,7 @@ def __init__(self): def numberToWords(self, num): """ + Pay attention to the handling of 0's :type num: int :rtype: str """ @@ -55,24 +57,22 @@ def numberToWords(self, num): ret = [] self.toWords(num, ret) + ret = filter(lambda x: x, ret) # filter zeros return " ".join(map(str, ret)) def toWords(self, num, ret): - sigs = [1000000000, 1000000, 1000, 100] - for sig in sigs: - num = self.partial(num, sig, ret) + SIGS = [1000000000, 1000000, 1000, 100] + for SIG in SIGS: + num = self.partial_parse(num, SIG, ret) - ten = 10 - if num/ten > 1: - ret.append(self.m[num/ten*ten]) - if num%ten != 0: - ret.append(self.m[num%ten]) - elif num/ten == 1: - ret.append(self.m[num]) - elif num != 0: + TEN = 10 + if num/TEN > 1: + ret.append(self.m[(num/TEN)*TEN]) + ret.append(self.m[num%TEN]) + else: ret.append(self.m[num]) - def partial(self, num, sig, ret): + def partial_parse(self, num, sig, ret): if num/sig: pre = [] self.toWords(num/sig, pre) From a5c9637e78d6375396553f09a6879c4d7e5767b0 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 17 Dec 2015 13:38:15 -0800 Subject: [PATCH 123/585] topolocial sorting --- 269 Alien Dictionary.py | 75 +++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/269 Alien Dictionary.py b/269 Alien Dictionary.py index 15517bd..8dc051c 100644 --- a/269 Alien Dictionary.py +++ b/269 Alien Dictionary.py @@ -1,6 +1,8 @@ """ Premium Question """ +from collections import defaultdict + __author__ = 'Daniel' @@ -10,20 +12,52 @@ def alienOrder(self, words): :type words: List[str] :rtype: str """ - V = {} - self.construct_graph(words, 0, len(words), 0, V) + V = self.construct_graph(words) visited = set() - marked = set() + pathset = set() ret = [] - - for k in V.keys(): - if k not in visited: - if not self.topological_dfs(V, k, visited, marked, ret): + for v in V.keys(): + if v not in visited: + if not self.topo_dfs(V, v, visited, pathset, ret): return "" return "".join(reversed(ret)) - def construct_graph(self, words, up, down, ptr, V): + def construct_graph(self, words): + V = defaultdict(list) + for i in xrange(len(words)-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 + + return V + + def topo_dfs(self, V, v, visited, pathset, ret): + """ + Topological sort + :param V: Vertices HashMap + :param v: currently visiting letter + :param visited: visited letters + :param pathset: marked predecessor in the path + :param ret: the path, ordered topologically + :return: whether contains cycles + """ + if v in pathset: + return False + + pathset.add(v) + for nbr in V[v]: + if nbr not in visited: + if not self.topo_dfs(V, nbr, visited, pathset, ret): + return False + + pathset.remove(v) + visited.add(v) + ret.append(v) + return True + + def construct_graph_tedious(self, words, up, down, ptr, V): """ :param words: :param up: upper bound @@ -44,35 +78,12 @@ def construct_graph(self, words, up, down, ptr, V): while j < down and ptr < len(words[j]) and words[j][ptr] == words[i][ptr]: j += 1 - self.construct_graph(words, i, j, ptr+1, V) + self.construct_graph_tedious(words, i, j, ptr+1, V) if j < down and ptr < len(words[j]): V[words[i][ptr]].append(words[j][ptr]) i = j - def topological_dfs(self, V, cur, visited, pathset, ret): - """ - :param V: Vertices HashMap - :param cur: currently visiting letter - :param visited: visited letters - :param pathset: marked predecessor in the path - :param ret: the path, ordered topologically - :return: whether contains cycles - """ - if cur in pathset: - return False - - pathset.add(cur) - for nei in V[cur]: - if nei not in visited: - if not self.topological_dfs(V, nei, visited, pathset, ret): - return False - - pathset.remove(cur) - visited.add(cur) - ret.append(cur) - return True - if __name__ == "__main__": lst = ["ze", "yf", "xd", "wd", "vd", "ua", "tt", "sz", "rd", "qd", "pz", "op", "nw", "mt", "ln", "ko", "jm", "il", From 929977f1aa233831ea9d9ead5f0762c5f5eb6a66 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 17 Dec 2015 13:55:57 -0800 Subject: [PATCH 124/585] read4 --- 157 Read N Characters Given Read4.py | 14 +++++++------- ...racters Given Read4 II - Call multiple times.py | 14 ++++---------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/157 Read N Characters Given Read4.py b/157 Read N Characters Given Read4.py index 9f8f312..a503a3b 100644 --- a/157 Read N Characters Given Read4.py +++ b/157 Read N Characters Given Read4.py @@ -19,7 +19,10 @@ def read(self, buf, n): """ read n chars to buf Algorithm: - Two ptrs + Two dimensions + 1st dim: buf full or not + 2nd dim: buf4 full or not + :type buf: Destination buffer (List[str]) :type n: Maximum number of characters to read (int) :rtype: The number of characters read (int) @@ -29,14 +32,11 @@ def read(self, buf, n): buf4 = ["" for _ in xrange(4)] r = read4(buf4) if idx+r < n: - for i in xrange(r): - buf[idx+i] = buf4[i] + buf[idx:idx+r] = buf4[:r] idx += r - if r < 4: - break + if r < 4: break else: - for i in xrange(idx, n): - buf[i] = buf4[i-idx] + buf[idx:n] = buf4[:n-idx] idx = n return idx diff --git a/158 Read N Characters Given Read4 II - Call multiple times.py b/158 Read N Characters Given Read4 II - Call multiple times.py index 778414b..c52a1df 100644 --- a/158 Read N Characters Given Read4 II - Call multiple times.py +++ b/158 Read N Characters Given Read4 II - Call multiple times.py @@ -27,8 +27,7 @@ def read(self, buf, n): :rtype: The number of characters read (int) """ l = min(len(self.prev), n) - for i in xrange(l): - buf[i] = self.prev[i] + buf[:l] = self.prev[:l] self.prev = self.prev[l:] # pitfall self.prev = [] idx = l # the next reading @@ -36,16 +35,11 @@ def read(self, buf, n): buf4 = ["" for _ in xrange(4)] r = read4(buf4) if idx+r < n: - for i in xrange(idx, idx+r): - buf[i] = buf4[i-idx] - + buf[idx:idx+r] = buf4[:r] idx += r - if r < 4: - return idx + if r < 4: return idx else: - for i in xrange(idx, n): - buf[i] = buf4[i-idx] - + buf[idx:n] = buf4[:n-idx] self.prev = buf4[n-idx:r] # pitfall buf4[n-idx:] idx = n From 8dd94bd006ec4fc896f9b22fba4ce95ed820f987 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 17 Dec 2015 14:07:38 -0800 Subject: [PATCH 125/585] graph tree --- 261 Graph Valid Tree.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/261 Graph Valid Tree.py b/261 Graph Valid Tree.py index 59e2fa5..4d1f101 100644 --- a/261 Graph Valid Tree.py +++ b/261 Graph Valid Tree.py @@ -9,35 +9,38 @@ class Solution(object): def validTree(self, n, edges): """ + A graph is a tree: + 1. no cycle + 2. all connected :type n: int - :edges: List[List[int] + :type edges: List[List[int] :rtype: bool """ if not edges: return n in (0, 1) V = defaultdict(list) - for edge in edges: - V[edge[0]].append(edge[1]) - V[edge[1]].append(edge[0]) + for e in edges: + V[e[0]].append(e[1]) + V[e[1]].append(e[0]) visited = set() - path_set = set() - if not self.dfs(V, edges[0][0], None, visited, path_set): + pathset = set() + if not self.dfs(V, edges[0][0], None, pathset, visited): return False return len(visited) == n - def dfs(self, V, k, pi, visited, path_set): - if k in path_set: + def dfs(self, V, v, pi, pathset, visited): + if v in pathset: return False - path_set.add(k) - for neighbor in V[k]: - if neighbor != pi: - if not self.dfs(V, neighbor, k, visited, path_set): + pathset.add(v) + for nbr in V[v]: + if nbr != pi: # since undirected graph + if not self.dfs(V, nbr, v, pathset, visited): return False - path_set.remove(k) - visited.add(k) + pathset.remove(v) + visited.add(v) return True \ No newline at end of file From 245fec095e77c835c5f31bee5930656ce34b4ef1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 18 Dec 2015 21:41:46 -0800 Subject: [PATCH 126/585] brainteaser --- 319 Bulb Switcher.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 319 Bulb Switcher.py diff --git a/319 Bulb Switcher.py b/319 Bulb Switcher.py new file mode 100644 index 0000000..4e64530 --- /dev/null +++ b/319 Bulb Switcher.py @@ -0,0 +1,33 @@ +""" +There are n bulbs that are initially off. You first turn on all the bulbs. Then, you turn off every second bulb. On the +third round, you toggle every third bulb (turning on if it's off or turning off if it's on). For the nth round, you only +toggle the last bulb. Find how many bulbs are on after n rounds. + +Example: + +Given n = 3. + +At first, the three bulbs are [off, off, off]. +After first round, the three bulbs are [on, on, on]. +After second round, the three bulbs are [on, off, on]. +After third round, the three bulbs are [on, off, off]. + +So you should return 1, because there is only one bulb is on. +""" +import math + +__author__ = 'Daniel' + + +class Solution(object): + def bulbSwitch(self, n): + """ + Only bulbs with index being a perfect square number toggled odd number of times + Brainteaser + :type n: int + :rtype: int + """ + cnt = int(math.sqrt(n)) + return cnt + + From 1eeee675ab46a1fdebc5d454b00b601115ac63da Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 21 Dec 2015 00:29:57 -0800 Subject: [PATCH 127/585] update merge --- 088 Merge Sorted Array.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/088 Merge Sorted Array.py b/088 Merge Sorted Array.py index 40e3df2..85875a1 100644 --- a/088 Merge Sorted Array.py +++ b/088 Merge Sorted Array.py @@ -6,7 +6,9 @@ number of elements initialized in A and B are m and n respectively. """ __author__ = 'Danyang' -class Solution: + + +class Solution(object): def merge(self, A, m, B, n): """ array, ascending order. @@ -24,9 +26,9 @@ def merge(self, A, m, B, n): j = n-1 closed = m+n - while i>=0 and j>=0: + while i >= 0 and j >= 0: closed -= 1 - if A[i]>B[j]: + if A[i] > B[j]: A[closed] = A[i] i -= 1 else: @@ -35,13 +37,5 @@ def merge(self, A, m, B, n): # either-or # dangling - while j>=0: - closed -= 1 - A[closed] = B[j] - j -= 1 - - # dangling - while i>=0: - closed -= 1 - A[closed] = A[i] - i -= 1 + if j >= 0: A[:closed] = B[:j+1] + # if i >= 0: A[:closed] = A[:i+1] From b97dd9ad604f3833faa5aa69cf1770714b37950c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 27 Dec 2015 13:33:43 -0800 Subject: [PATCH 128/585] add stream version of edit distance --- 161 One Edit Distance.py | 52 ++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/161 One Edit Distance.py b/161 One Edit Distance.py index f90136a..cdee136 100644 --- a/161 One Edit Distance.py +++ b/161 One Edit Distance.py @@ -1,5 +1,6 @@ """ Premium question +Non-dp version of edit distance """ __author__ = 'Daniel' @@ -13,28 +14,49 @@ def isOneEditDistance(self, s, t): :type t: str :rtype: bool """ - l_s = len(s) - l_t = len(t) - if abs(l_s-l_t) > 1: - return False + m, n = len(s), len(t) + if m > n: return self.isOneEditDistance(t, s) + if n-m > 1: return False - if l_s > l_t: - s, t = t, s - l_s, l_t = l_t, l_s - - error = 0 + diff = 0 i, j = 0, 0 - while i < l_s and j < l_t: + while i < m and j < n and diff < 2: if s[i] == t[j]: i += 1 j += 1 else: - if l_s != l_t: - j += 1 - else: + if m != n: + j += 1 # delete + else: # replace s[i] i += 1 j += 1 - error += 1 + diff += 1 + + return diff == 1 or diff == 0 and m != n + + +class Solution1(object): + def isOneEditDistance(self, s, t): + """ + Iterator version + """ + m, n = len(s), len(t) + if m > n: return self.isOneEditDistance(t, s) + if n-m > 1: return False + + diff = 0 + i, j = iter(s), iter(t) + a, b = next(i, None), next(j, None) + while a and b and diff < 2: + if a == b: + a, b = next(i, None), next(j, None) + else: + if m != n: + b = next(j, None) + else: + a, b = next(i, None), next(j, None) + + diff += 1 - return error == 1 or error == 0 and l_s != l_t + return diff == 1 or diff == 0 and m != n From dbb7567c9d6ccab83b4429f86e15f797968021e6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 1 Jan 2016 23:19:04 -0800 Subject: [PATCH 129/585] simple dfs --- ...ected Components in an Undirected Graph.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 323 Number of Connected Components in an Undirected Graph.py diff --git a/323 Number of Connected Components in an Undirected Graph.py b/323 Number of Connected Components in an Undirected Graph.py new file mode 100644 index 0000000..ba542af --- /dev/null +++ b/323 Number of Connected Components in an Undirected Graph.py @@ -0,0 +1,33 @@ +""" +Premium Question +simple DFS +""" +__author__ = 'Daniel' + + +class Solution(object): + def countComponents(self, n, edges): + """ + :type n: int + :type edges: List[List[int]] + :rtype: int + """ + V = [[] for _ in xrange(n)] + for e in edges: + V[e[0]].append(e[1]) + V[e[1]].append(e[0]) + + visited = [False for _ in xrange(n)] + cnt = 0 + for v in xrange(n): + if not visited[v]: + cnt += 1 + self.dfs(V, v, visited) + + return cnt + + def dfs(self, V, v, visited): + visited[v] = True + for nbr in V[v]: + if not visited[nbr]: + self.dfs(V, nbr, visited) From 432c2fbb5d0d80c09bea2ecc35a2a50934bc2cb0 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 1 Jan 2016 23:33:02 -0800 Subject: [PATCH 130/585] forward dp --- 322 Coin Change.py | 74 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 322 Coin Change.py diff --git a/322 Coin Change.py b/322 Coin Change.py new file mode 100644 index 0000000..4be7d85 --- /dev/null +++ b/322 Coin Change.py @@ -0,0 +1,74 @@ +""" +You are given coins of different denominations and a total amount of money amount. Write a function to compute the +fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any +combination of the coins, return -1. + +Example 1: +coins = [1, 2, 5], amount = 11 +return 3 (11 = 5 + 5 + 1) + +Example 2: +coins = [2], amount = 3 +return -1. + +Note: +You may assume that you have an infinite number of each kind of coin. +""" +import sys + +__author__ = 'Daniel' + + +class Solution(object): + def coinChange(self, coins, amount): + """ + DP with early prune + Let F[i] be the fewest number of coins make to i + F[i+k] = min(F[i]+1, \forall k if F[i]) + O(NM) + :type coins: List[int] + :type amount: int + :rtype: int + """ + if amount == 0: + return 0 + + F = [sys.maxint for _ in xrange(amount+1)] + for k in coins: + if k < amount+1: + F[k] = 1 + + for i in xrange(1, amount+1): + if F[i] != sys.maxint: + for k in coins: + if i+k <= amount: + F[i+k] = min(F[i+k], F[i]+1) + + return F[amount] if F[amount] != sys.maxint else -1 + + +class SolutionTLE(object): + def coinChange(self, coins, amount): + """ + Let F[i] be the fewest number of coins make to i + F[i] = min(F[i-k]+1, \forall k) + O(NM) + :type coins: List[int] + :type amount: int + :rtype: int + """ + F = [sys.maxint for _ in xrange(amount+1)] + for k in coins: + if k < amount + 1: + F[k] = 1 + + for i in xrange(1, amount+1): + for k in coins: + if i-k > 0 and F[i-k] != sys.maxint: + F[i] = min(F[i], F[i-k]+1) + + return F[amount] if F[amount] != sys.maxint else -1 + + +if __name__ == "__main__": + assert Solution().coinChange([243, 291, 335, 209, 177, 345, 114, 91, 313, 331], 7367) == 23 \ No newline at end of file From fb64aeab0a4abb27d0eb8c903dacaba3b31de246 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 3 Jan 2016 19:23:42 -0800 Subject: [PATCH 131/585] backtracking --- 320 Generalized Abbreviation.py | 66 +++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 320 Generalized Abbreviation.py diff --git a/320 Generalized Abbreviation.py b/320 Generalized Abbreviation.py new file mode 100644 index 0000000..868d72c --- /dev/null +++ b/320 Generalized Abbreviation.py @@ -0,0 +1,66 @@ +""" +Premium Question +Backtracking +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class Solution(object): + def generateAbbreviations(self, word): + """ + backtracking, pivoting letter + :type word: str + :rtype: List[str] + """ + if not word: + return [""] + + ret = [] + for i in xrange(len(word)+1): + left_num = str(i) if i else "" + for right in self.generateAbbreviations(word[i+1:]): + cur = left_num + word[i:i+1] + right + ret.append(cur) + + return ret + + +class SolutionTLE(object): + def __init__(self): + self.cache = defaultdict(list) + + def generateAbbreviations(self, word): + """ + Cached, brute force + Two-way backtracking, pivoting number + :type word: str + :rtype: List[str] + """ + return list(set(self.dfs(word))) + + def dfs(self, word): + if word not in self.cache: + ret = [] + for l in xrange(1, len(word)+1): + pivot = str(l) + for i in xrange(len(word)-l+1): + lefts = self.dfs(word[:i]) + rights = self.dfs(word[i+l:]) + for left in lefts: + for right in rights: + if left and left[-1].isdigit() or right and right[0].isdigit(): + continue + + ret.append(left+pivot+right) + + ret.append(word) + self.cache[word] = ret + + return self.cache[word] + + +if __name__ == "__main__": + assert Solution().generateAbbreviations("word") == ['word', 'wor1', 'wo1d', 'wo2', 'w1rd', 'w1r1', 'w2d', 'w3', + '1ord', '1or1', '1o1d', '1o2', '2rd', '2r1', '3d', '4'] \ No newline at end of file From 459cfdd47ed25203de3020ec2d23724c8c602ee8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 4 Jan 2016 16:34:10 -0800 Subject: [PATCH 132/585] wiggle sort --- 280 Wiggle Sort.py | 3 ++ 324 Wiggle Sort II.py | 111 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 324 Wiggle Sort II.py diff --git a/280 Wiggle Sort.py b/280 Wiggle Sort.py index 6022760..7c3d30e 100644 --- a/280 Wiggle Sort.py +++ b/280 Wiggle Sort.py @@ -1,5 +1,6 @@ """ Premium Question +nums[0] <= nums[1] >= nums[2] <= nums[3]... """ __author__ = 'Daniel' @@ -8,6 +9,8 @@ class Solution(object): def wiggleSort(self, nums): """ Solve by enumerating examples + Time: O(n lg n) + Space: O(1) :type nums: List[int] :rtype: void Do not return anything, modify nums in-place instead. """ diff --git a/324 Wiggle Sort II.py b/324 Wiggle Sort II.py new file mode 100644 index 0000000..e3294d3 --- /dev/null +++ b/324 Wiggle Sort II.py @@ -0,0 +1,111 @@ +""" +iven an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3].... + +Example: +(1) Given nums = [1, 5, 1, 1, 6, 4], one possible answer is [1, 4, 1, 5, 1, 6]. +(2) Given nums = [1, 3, 2, 2, 3, 1], 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? +""" +__author__ = 'Daniel' + + +class Solution(object): + def wiggleSort(self, A): + """ + 1. Quick selection for finding median (Average O(n)) + 2. Three-way partitioning to split the data + 3. Re-mapping the index to do in-place partitioning + Average time O(n) + Space O(1) + :type A: List[int] + :rtype: in-place + """ + n = len(A) + median_idx = self.find_kth(A, 0, n, n/2) + v = A[median_idx] + + def idx(i): + return (2*i+1) % (n|1) + + l = -1 + r = n + i = 0 + while i < r: + if A[idx(i)] > v: + l += 1 + A[idx(l)], A[idx(i)] = A[idx(i)], A[idx(l)] + i += 1 + elif A[idx(i)] == v: + i += 1 + else: + r -= 1 + A[idx(r)], A[idx(i)] = A[idx(i)], A[idx(r)] + + def pivot(self, A, lo, hi, pidx=None): + lt = lo-1 + gt = hi + if not pidx: pidx = lo + + v = A[pidx] + i = lo + while i < gt: + if A[i] < v: + lt += 1 + A[lt], A[i] = A[i], A[lt] + i += 1 + elif A[i] == v: + i += 1 + else: + gt -= 1 + A[gt], A[i] = A[i], A[gt] + + return lt, gt + + def find_kth(self, A, lo, hi, k): + if lo >= hi: return + + lt, gt = self.pivot(A, lo, hi) + if lt < k < gt: + return k + if k <= lt: + return self.find_kth(A, lo, lt+1, k) + else: + return self.find_kth(A, gt, hi, k) + + +class SolutionSort(object): + def wiggleSort(self, nums): + """ + Sort-based: interleave the small half and large half + + Could they be "equal to"? That would require some number M to appear both in the smaller and the larger half. + It shall be the largest in the smaller half and the smallest in the larger half. + + To deal with duplicate median element cases (e.g. [4 5 5 6]), interleave in a reverse order + :type nums: List[int] + :rtype: void Do not return anything, modify nums in-place instead. + """ + n = len(nums) + A = sorted(nums) + + j, k = (n-1)/2, n-1 + for i in xrange(len(nums)): + if i%2 == 0: + nums[i] = A[j] + j -= 1 + else: + nums[i] = A[k] + k -= 1 + + +if __name__ == "__main__": + # A = [1, 5, 1, 1, 6, 4] + A = [3,2,1,1,3,2] + Solution().wiggleSort(A) + print A + From 6ca082282f81666132c833eec26609dc0c9e8e99 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Jan 2016 00:21:28 -0800 Subject: [PATCH 133/585] search --- 325 Maximum Size Subarray Sum Equals k.py | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 325 Maximum Size Subarray Sum Equals k.py diff --git a/325 Maximum Size Subarray Sum Equals k.py b/325 Maximum Size Subarray Sum Equals k.py new file mode 100644 index 0000000..9a77477 --- /dev/null +++ b/325 Maximum Size Subarray Sum Equals k.py @@ -0,0 +1,27 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution(object): + def maxSubArrayLen(self, A, k): + """ + Search problem + :type A: List[int] + :type k: int + :rtype: int + """ + m = {0: -1} # initial condition, sum -> idx + maxa = 0 + s = 0 + for i in xrange(len(A)): + s += A[i] + t = s - k # s - t = k + if t in m: + maxa = max(maxa, i - m[t]) + + if s not in m: + m[s] = i + + return maxa \ No newline at end of file From 24c5bf03c6fea5b951dfeecf12dd3b8b6953e299 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Jan 2016 12:40:28 -0800 Subject: [PATCH 134/585] TLE --- 321 Create Maximum Number.py | 86 ++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 321 Create Maximum Number.py diff --git a/321 Create Maximum Number.py b/321 Create Maximum Number.py new file mode 100644 index 0000000..459e47e --- /dev/null +++ b/321 Create Maximum Number.py @@ -0,0 +1,86 @@ +""" +Given two arrays of length m and n with digits 0-9 representing two numbers. Create the maximum number of length k <= +m + n from digits of the two. The relative order of the digits from the same array must be preserved. Return an array +of the k digits. You should try to optimize your time and space complexity. + +Example 1: +nums1 = [3, 4, 6, 5] +nums2 = [9, 1, 2, 5, 8, 3] +k = 5 +return [9, 8, 6, 5, 3] + +Example 2: +nums1 = [6, 7] +nums2 = [6, 0, 4] +k = 5 +return [6, 7, 6, 0, 4] + +Example 3: +nums1 = [3, 9] +nums2 = [8, 9] +k = 3 +return [9, 8, 9] +""" +__author__ = 'Daniel' + + +class SolutionTLE(object): + def maxNumber(self, nums1, nums2, k): + """ + http://algobox.org/2015/12/24/create-maximum-number/ + O(kN(N+M)) + :type nums1: List[int] + :type nums2: List[int] + :type k: int + :rtype: List[int] + """ + maxa = [] + n1, n2 = len(nums1), len(nums2) + for l1 in xrange(min(n1, k)+1): + l2 = k - l1 + assert l2 >= 0 + A1, A2 = self.maxNumberSingle(nums1, l1), self.maxNumberSingle(nums2, l2) + cur = self.maxNumberDual(A1, A2) + if not maxa or self.eval(maxa) < self.eval(cur): + maxa = cur + + return maxa + + def eval(self, lst): + return int("".join(map(str, lst))) + + def maxNumberSingle(self, A, k): + """ + maxNumber of k elements from a single list A + """ + stk = [] + n = len(A) + for i in xrange(n): + while stk and len(stk)-1+(n-1-i+1) >= k and stk[-1] < A[i]: stk.pop() + if len(stk) < k: + stk.append(A[i]) + + return stk + + def maxNumberDual(self, A1, A2): + """ + maxNumber of all elements from dual lists A1 and A2. + """ + ret = [] + p1, p2 = 0, 0 + while p1 < len(A1) and p2 < len(A2): + ahead1, ahead2 = p1, p2 + while ahead1 < len(A1) and ahead2 < len(A2) and A1[ahead1] == A2[ahead2]: + ahead1, ahead2 = ahead1+1, ahead2+1 + + if ahead2 >= len(A2) or (ahead1 < len(A1) and A1[ahead1] > A2[ahead2]): + ret.append(A1[p1]) + p1 += 1 + else: + ret.append(A2[p2]) + p2 += 1 + + # dangling + ret.extend(A1[p1:]) + ret.extend(A2[p2:]) + return ret From 9184a1fec2071d0a531c5e0e72ac1e5f2aca68b4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Jan 2016 14:49:37 -0800 Subject: [PATCH 135/585] reformat --- 010 Container With Most Water.py | 2 +- 020 Valid Parentheses.py | 9 ++++--- 031 Longest Valid Parentheses.py | 35 ++++++++++++++------------ 068 Simplify Path.py | 28 +++++++++++++-------- 091 Decode Ways.py | 42 ++++++++++++++++--------------- 301 Remove Invalid Parentheses.py | 35 +++++++++++++------------- 6 files changed, 83 insertions(+), 68 deletions(-) diff --git a/010 Container With Most Water.py b/010 Container With Most Water.py index 4ecd1aa..e55966f 100644 --- a/010 Container With Most Water.py +++ b/010 Container With Most Water.py @@ -21,7 +21,7 @@ def maxArea(self, height): start = 0 end = len(height)-1 - max_area = -1<<32 + max_area = -1 << 32 while start < end: area = min(height[start], height[end])*(end-start) max_area = max(area, max_area) diff --git a/020 Valid Parentheses.py b/020 Valid Parentheses.py index 5ebf84a..21d5942 100644 --- a/020 Valid Parentheses.py +++ b/020 Valid Parentheses.py @@ -4,6 +4,8 @@ The brackets must close in the correct order, "()" and "()[]{}" are all valid but "(]" and "([)]" are not. """ __author__ = 'Danyang' + + class Solution: def isValid(self, s): """ @@ -20,10 +22,11 @@ def isValid(self, s): if element in put_set: stack.append(pair[element]) elif element in pop_set: - if not stack or element!=stack.pop(): # check NullPointer, otherwise, IndexError: pop from empty list + if not stack or element != stack.pop(): # check NullPointer, otherwise, IndexError: pop from empty list return False return True if not stack else False -if __name__=="__main__": - Solution().isValid("()") \ No newline at end of file + +if __name__ == "__main__": + assert Solution().isValid("()") \ No newline at end of file diff --git a/031 Longest Valid Parentheses.py b/031 Longest Valid Parentheses.py index 3e0b7e1..baefde5 100644 --- a/031 Longest Valid Parentheses.py +++ b/031 Longest Valid Parentheses.py @@ -7,7 +7,9 @@ Another example is ")()())", where the longest valid parentheses substring is "()()", which has length = 4. """ __author__ = 'Danyang' -class Solution: + + +class Solution(object): def longestValidParentheses(self, s): """ Stack holds the index of unpaired brackets @@ -23,22 +25,23 @@ def longestValidParentheses(self, s): :param s: a string :return: an integer """ - stack = [] # hold unpaired bracket, either ( or ) - longest = 0 - for ind, char in enumerate(s): - if char==")" and stack and s[stack[-1]]=="(": # ), and non-empty stack - stack.pop() - if not stack: - longest = ind+1 + stk = [] # hold the INDEX of UNPAIRED bracket, either ( or ) + maxa = 0 + for idx, val in enumerate(s): + if val == ")" and stk and s[stk[-1]] == "(": + stk.pop() + if not stk: + maxa = max(maxa, idx+1) else: - longest = max(longest, ind-stack[-1]) + maxa = max(maxa, idx-stk[-1]) else: - stack.append(ind) + stk.append(idx) + + return maxa - return longest -if __name__=="__main__": - assert Solution().longestValidParentheses("(()()")==4 - assert Solution().longestValidParentheses("()(()")==2 - assert Solution().longestValidParentheses("(()")==2 - assert Solution().longestValidParentheses(")()())")==4 \ No newline at end of file +if __name__ == "__main__": + assert Solution().longestValidParentheses("(()()") == 4 + assert Solution().longestValidParentheses("()(()") == 2 + assert Solution().longestValidParentheses("(()") == 2 + assert Solution().longestValidParentheses(")()())") == 4 \ No newline at end of file diff --git a/068 Simplify Path.py b/068 Simplify Path.py index b14aae8..0a786de 100644 --- a/068 Simplify Path.py +++ b/068 Simplify Path.py @@ -13,7 +13,9 @@ In this case, you should ignore redundant slashes and return "/home/foo". """ __author__ = 'Danyang' -class Solution: + + +class Solution(object): def simplifyPath(self, path): """ use "." as intermediate @@ -24,13 +26,16 @@ def simplifyPath(self, path): path = path.split("/") path = filter(lambda x: x not in ("", " ", "."), path) - for ind, val in enumerate(path): # some unexpected error in memory - if val=="..": - path[ind] = "." + # modify the content of the list, not the structure. + for idx in xrange(len(path)): + val = path[idx] + if val == "..": + path[idx] = "." - i = 1 - while ind-i>=0 and path[ind-i]==".": i += 1 - if ind-i>=0: path[ind-i] = "." # avoid unexpected path[-1] + # rm a previous meaningful part + i = idx-1 + while i >= 0 and path[i] == ".": i -= 1 + if i >= 0: path[i] = "." # avoid path[-1] path = filter(lambda x: x not in (".",), path) @@ -40,7 +45,8 @@ def simplifyPath(self, path): path = map(lambda x: "/"+x, path) return "".join(path) -if __name__=="__main__": - assert Solution().simplifyPath("/a/./b///../c/../././../d/..//../e/./f/./g/././//.//h///././/..///")=="/e/f/g" - assert Solution().simplifyPath("/a/./b/../../c/")=="/c" - assert Solution().simplifyPath("/../")=="/" \ No newline at end of file + +if __name__ == "__main__": + assert Solution().simplifyPath("/a/./b///../c/../././../d/..//../e/./f/./g/././//.//h///././/..///") == "/e/f/g" + assert Solution().simplifyPath("/a/./b/../../c/") == "/c" + assert Solution().simplifyPath("/../") == "/" \ No newline at end of file diff --git a/091 Decode Ways.py b/091 Decode Ways.py index 7d3741e..6fccfac 100644 --- a/091 Decode Ways.py +++ b/091 Decode Ways.py @@ -13,13 +13,16 @@ The number of ways decoding "12" is 2. """ __author__ = 'Danyang' -class Solution: + + +class Solution(object): def numDecodings(self, s): """ - dp + F + Let F[i] be the number of decode ways for s[:i] - 1 2 2 3 1 2 2 3 1 1 2 ? ? - dp[i] = (dp[i-1]) + optional(dp[i-2]) + F[i] = (F[i-1]) + optional(F[i-2])) notice the special handling for "0 @@ -32,27 +35,26 @@ def numDecodings(self, s): n = len(s) if not s: return 0 - dp = [0 for _ in xrange(n+1)] - dp[0] = 1 - dp[1] = 1 + F = [0 for _ in xrange(n+1)] + F[0] = 1 + F[1] = 1 for i in xrange(2, n+1): - if s[i-1]!="0": - dp[i] = dp[i-1] - if 10<=int(s[i-2]+s[i-1])<27: - dp[i] += dp[i-2] - + if s[i-1] != "0": + F[i] = F[i-1] + if 10 <= int(s[i-2]+s[i-1]) < 27: + F[i] += F[i-2] else: # 0 is special - if s[i-2] not in ("1", "2"): - return 0 + if s[i-2] in ("1", "2"): + F[i] = F[i-2] else: - dp[i] = dp[i-2] + return 0 + return F[-1] - return dp[-1] -if __name__=="__main__": - assert Solution().numDecodings("10")==1 - assert Solution().numDecodings("27")==1 - assert Solution().numDecodings("12")==2 - assert Solution().numDecodings("0")==0 \ No newline at end of file +if __name__ == "__main__": + assert Solution().numDecodings("10") == 1 + assert Solution().numDecodings("27") == 1 + assert Solution().numDecodings("12") == 2 + assert Solution().numDecodings("0") == 0 \ No newline at end of file diff --git a/301 Remove Invalid Parentheses.py b/301 Remove Invalid Parentheses.py index cd833c6..8db361a 100644 --- a/301 Remove Invalid Parentheses.py +++ b/301 Remove Invalid Parentheses.py @@ -30,46 +30,47 @@ def minrm(self, s): returns minimal number of removals """ rmcnt = 0 - l = 0 + left = 0 for c in s: if c == "(": - l += 1 + left += 1 elif c == ")": - l -= 1 - if l < 0: - l = 0 + if left > 0: + left -= 1 + else: rmcnt += 1 - rmcnt += l + rmcnt += left return rmcnt - def dfs(self, s, cur, l, last_rm, i, rmcnt, ret): + def dfs(self, s, cur, left, pi, i, rmcnt, ret): """ Remove parenthesis backtracking, post-check :param s: original string :param cur: current string builder - :param l: number of remaining left parentheses in s[0..i] - :param last_rm: last removed char + :param left: number of remaining left parentheses in s[0..i] not consumed by ")" + :param pi: last removed char + :param i: current index :param rmcnt: number of remaining removals needed :param ret: results """ - if l < 0 or rmcnt < 0 or i > len(s): + if left < 0 or rmcnt < 0 or i > len(s): return if i == len(s): - if rmcnt == 0 and l == 0: + if rmcnt == 0 and left == 0: ret.append(cur) return if s[i] not in ("(", ")"): # skip non-parenthesis - self.dfs(s, cur+s[i], l, None, i+1, rmcnt, ret) + self.dfs(s, cur+s[i], left, None, i+1, rmcnt, ret) else: - if last_rm == s[i]: # jump, if rm, rm them all to avoid duplication - while i < len(s) and last_rm and last_rm == s[i]: i, rmcnt = i+1, rmcnt-1 - self.dfs(s, cur, l, last_rm, i, rmcnt, ret) + if pi == s[i]: # jump, if rm, rm them all to avoid duplication + while i < len(s) and pi and pi == s[i]: i, rmcnt = i+1, rmcnt-1 + self.dfs(s, cur, left, pi, i, rmcnt, ret) else: - self.dfs(s, cur, l, s[i], i+1, rmcnt-1, ret) - L = l+1 if s[i] == "(" else l-1 # consume "(" + self.dfs(s, cur, left, s[i], i+1, rmcnt-1, ret) + L = left+1 if s[i] == "(" else left-1 # consume "(" self.dfs(s, cur+s[i], L, None, i+1, rmcnt, ret) # put From b1280d156f7c03f463cee5dd12c4343b9954ef4f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 5 Jan 2016 21:38:16 -0800 Subject: [PATCH 136/585] update dp solution --- 061 Unique Paths.py | 66 ++++++++++++++++++++++---------- 125 Valid Palindrome.py | 7 ++-- 209 Minimum Size Subarray Sum.py | 21 +++++----- 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/061 Unique Paths.py b/061 Unique Paths.py index c70327a..ff4ae0d 100644 --- a/061 Unique Paths.py +++ b/061 Unique Paths.py @@ -11,31 +11,57 @@ Note: m and n will be at most 100. """ +import math __author__ = 'Danyang' -class Solution: + + +class Solution(object): def uniquePaths(self, m, n): + """ + Math solution: + if total m+n steps + (m+n) \choose m as down, the remain n as right. + + mCn = n!/m!(n-m)! + :param m: + :param n: + :return: + """ + m -= 1 + n -= 1 + return math.factorial(m+n) / (math.factorial(n) * math.factorial(m)) + + def uniquePathsDP(self, m, n): + F = [[0 for _ in xrange(n+1)] for _ in xrange(m+1)] + F[1][0] = 1 # dummy entry point + for i in xrange(1, m+1): + for j in xrange(1, n+1): + F[i][j] = F[i-1][j] + F[i][j-1] + + return F[m][n] + + def uniquePathsNormal(self, m, n): """ dp - path[i][j] = path[i-1][j] + path[i][j-1] + Let F be number of unique paths at position i, j + F[i][j] = F[i-1][j] + F[i][j-1] :param m: :param n: :return: an integer """ - path = [[0 for _ in range(n)] for _ in range(m)] - path[0][0] = 1 # start - - # path[i][j] = path[i-1][j] + path[i][j-1] - for i in range(m): - for j in range(n): - if i==0 and j==0: - continue - if i==0: - path[i][j] = path[i][j-1] - elif j==0: - path[i][j] = path[i-1][j] - else: - path[i][j] = path[i-1][j]+path[i][j-1] - return path[m-1][n-1] - -if __name__=="__main__": - print Solution().uniquePaths(3, 7) \ No newline at end of file + F = [[0 for _ in xrange(n)] for _ in xrange(m)] + F[0][0] = 1 # start + + # F[i][j] = F[i-1][j] + F[i][j-1] + for i in xrange(m): + for j in xrange(n): + if i == 0 and j == 0: continue + if i == 0: F[i][j] = F[i][j-1] + elif j == 0: F[i][j] = F[i-1][j] + else: F[i][j] = F[i-1][j]+F[i][j-1] + + return F[m-1][n-1] + + +if __name__ == "__main__": + assert Solution().uniquePaths(3, 7) == 28 \ No newline at end of file diff --git a/125 Valid Palindrome.py b/125 Valid Palindrome.py index f598be6..d76b13b 100644 --- a/125 Valid Palindrome.py +++ b/125 Valid Palindrome.py @@ -11,7 +11,9 @@ For the purpose of this problem, we define empty string as valid palindrome. """ __author__ = 'Danyang' -class Solution: + + +class Solution(object): def isPalindrome(self, s): """ @@ -25,5 +27,4 @@ def isPalindrome(self, s): if not s: return True - s2 = s[::-1] - return s2==s \ No newline at end of file + return s == s[::-1] \ No newline at end of file diff --git a/209 Minimum Size Subarray Sum.py b/209 Minimum Size Subarray Sum.py index b40d33a..0c54d82 100644 --- a/209 Minimum Size Subarray Sum.py +++ b/209 Minimum Size Subarray Sum.py @@ -23,23 +23,20 @@ def minSubArrayLen(self, s, nums): """ n = len(nums) - f = [0 for _ in xrange(n+1)] + S = [0 for _ in xrange(n+1)] for i in xrange(1, n+1): - f[i] = f[i-1]+nums[i-1] + S[i] = S[i-1]+nums[i-1] - b, e = 0, 1 + lo, hi = 0, 1 mini = sys.maxint - while e <= n: - if f[e]-f[b] >= s: - mini = min(mini, e-b) - b += 1 + while hi <= n: + if S[hi]-S[lo] >= s: + mini = min(mini, hi-lo) + lo += 1 else: - e += 1 + hi += 1 - if mini == sys.maxint: - mini = 0 - - return mini + return mini if mini != sys.maxint else 0 if __name__ == "__main__": From adc25017403b4a6d97c2239e407387faad48dd69 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 7 Jan 2016 14:40:16 -0800 Subject: [PATCH 137/585] doc --- 076 Minimum Window Substring.py | 52 ++++++++++++++++------------- 104 Maximum Depth of Binary Tree.py | 13 ++++---- 111 Minimum Depth of Binary Tree.py | 21 ++++++------ 3 files changed, 45 insertions(+), 41 deletions(-) diff --git a/076 Minimum Window Substring.py b/076 Minimum Window Substring.py index 3800991..de29d9b 100644 --- a/076 Minimum Window Substring.py +++ b/076 Minimum Window Substring.py @@ -12,8 +12,12 @@ If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S. """ +import sys + __author__ = 'Danyang' -class Solution: + + +class Solution(object): def minWindow(self, S, T): """ Algorithm: @@ -24,37 +28,37 @@ def minWindow(self, S, T): :param T: str :return: str """ - min_window = [0, 1<<32] # [start, end) - w_chars = [0 for _ in range(256)] # window - T_CHARS = [0 for _ in range(256)] # 256 ascii, static + min_win = [0, sys.maxint] # [start, end) + w_cnt = [0 for _ in range(256)] # window + t_cnt = [0 for _ in range(256)] # 256 ascii, static for char in T: - T_CHARS[ord(char)] += 1 # remain static after construction + t_cnt[ord(char)] += 1 appeared_cnt = 0 - start_ptr = 0 - for end_ptr in xrange(len(S)): + lo = 0 + for hi in xrange(1, len(S)+1): # expand - val = S[end_ptr] - if T_CHARS[ord(val)]>0: - w_chars[ord(val)] += 1 - if T_CHARS[ord(val)]>0 and w_chars[ord(val)]<=T_CHARS[ord(val)]: - appeared_cnt += 1 # when to decrease appeared_cnt? + val = S[hi-1] + if t_cnt[ord(val)] > 0: + w_cnt[ord(val)] += 1 + + if t_cnt[ord(val)] > 0 and w_cnt[ord(val)] <= t_cnt[ord(val)]: + appeared_cnt += 1 # cache, determine when to decrease appeared_cnt # shrink - if appeared_cnt==len(T): # until find all - # while w_chars[ord(S[start_ptr])]>T_CHARS[ord(S[start_ptr])] or w_chars[ord(S[start_ptr])]<=0: - while w_chars[ord(S[start_ptr])]>T_CHARS[ord(S[start_ptr])] or T_CHARS[ord(S[start_ptr])]<=0: - w_chars[ord(S[start_ptr])] -= 1 # if negative, it doesn't matter - start_ptr += 1 + if appeared_cnt == len(T): # until find all + while w_cnt[ord(S[lo])] > t_cnt[ord(S[lo])] or t_cnt[ord(S[lo])] == 0: + if w_cnt[ord(S[lo])] > 0: w_cnt[ord(S[lo])] -= 1 + lo += 1 - # after shrinking, still valid window - if min_window[1]-min_window[0]>end_ptr-start_ptr+1: - min_window[0], min_window[1] = start_ptr, end_ptr+1 + if min_win[1]-min_win[0] > hi-lo: + min_win[0], min_win[1] = lo, hi - if min_window[1]==1<<32: + if min_win[1] == sys.maxint: return "" else: - return S[min_window[0]:min_window[1]] + return S[min_win[0]:min_win[1]] + -if __name__=="__main__": - assert Solution().minWindow("ADOBECODEBANC", "ABC")=="BANC" \ No newline at end of file +if __name__ == "__main__": + assert Solution().minWindow("ADOBECODEBANC", "ABC") == "BANC" \ No newline at end of file diff --git a/104 Maximum Depth of Binary Tree.py b/104 Maximum Depth of Binary Tree.py index 34fb57a..a19515d 100644 --- a/104 Maximum Depth of Binary Tree.py +++ b/104 Maximum Depth of Binary Tree.py @@ -4,14 +4,16 @@ The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. """ __author__ = 'Danyang' -# Definition for a binary tree node -class TreeNode: + + +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: + +class Solution(object): # @param root, a tree node # @return an integer def maxDepth(self, root): @@ -25,6 +27,5 @@ def fathom(self, root, depth): """ DFS """ - if not root: - return depth - return max(self.fathom(root.left, depth+1), self.fathom(root.right, depth+1)) + if not root: return depth + else: return max(self.fathom(root.left, depth+1), self.fathom(root.right, depth+1)) diff --git a/111 Minimum Depth of Binary Tree.py b/111 Minimum Depth of Binary Tree.py index 1739f4a..4fd023c 100644 --- a/111 Minimum Depth of Binary Tree.py +++ b/111 Minimum Depth of Binary Tree.py @@ -4,14 +4,16 @@ The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node. """ __author__ = 'Danyang' -# Definition for a binary tree node -class TreeNode: + + +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: + +class Solution(object): def minDepth(self, root): """ :param root: TreeNode @@ -23,11 +25,8 @@ def fathom(self, root, depth): """ DFS """ - if not root: - return depth # whether -1 or not depends on whether depth starts from 0 or 1 - if root.left is None and root.right is not None: - return self.fathom(root.right, depth+1) - if root.right is None and root.left is not None: - return self.fathom(root.left, depth+1) - - return min(self.fathom(root.left, depth+1), self.fathom(root.right, depth+1)) \ No newline at end of file + if not root: return depth + elif root.right and not root.left: return self.fathom(root.right, depth+1) + elif root.left and not root.right: return self.fathom(root.left, depth+1) + else: return min(self.fathom(root.left, depth+1), + self.fathom(root.right, depth+1)) \ No newline at end of file From 7cdddd5ac191390602ca11e985576f66ce5cfe4e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 8 Jan 2016 17:42:24 -0800 Subject: [PATCH 138/585] doc --- 042 Multiply Strings.py | 27 +++++++++++---------------- 311 Sparse Matrix Multiplication.py | 2 +- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/042 Multiply Strings.py b/042 Multiply Strings.py index 2bf45e5..6ff18bf 100644 --- a/042 Multiply Strings.py +++ b/042 Multiply Strings.py @@ -4,7 +4,9 @@ Note: The numbers can be arbitrarily large and are non-negative. """ __author__ = 'Danyang' -class Solution: + + +class Solution(object): def multiply(self, num1, num2): """ Google Phone Interview Question, 20 Sep 2013 @@ -25,7 +27,7 @@ def multiply(self, num1, num2): result = [] # pre processing - if len(num1) Date: Sat, 9 Jan 2016 23:31:04 -0800 Subject: [PATCH 139/585] test case --- 042 Multiply Strings.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/042 Multiply Strings.py b/042 Multiply Strings.py index 6ff18bf..818445a 100644 --- a/042 Multiply Strings.py +++ b/042 Multiply Strings.py @@ -115,8 +115,8 @@ def add(self, num1, num2): if __name__ == "__main__": solution = Solution() - # assert [1, 2]==solution.add([2,1], [9]) - #assert [1, 9, 9, 8]==solution.multiply_1_digit("9", "999") - #assert str(123*999)==solution.multiply("123", "999") - #assert str(0)==solution.multiply("0", "0") - assert str(123*456) == solution.multiply("123", "456") \ No newline at end of file + assert [1, 2]==solution.add([2,1], [9]) + assert [1, 9, 9, 8]==solution.multiply_1_digit("9", "999") + assert str(123*999)==solution.multiply("123", "999") + assert str(0)==solution.multiply("0", "0") + assert str(123*456) == solution.multiply("123", "456") From 87444fee5836ae6f3756b98f886032be97b3a184 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 19 Jan 2016 23:14:14 -0800 Subject: [PATCH 140/585] skyline refactor --- 218 The Skyline Problem.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/218 The Skyline Problem.py b/218 The Skyline Problem.py index 0ae19da..cc269b8 100644 --- a/218 The Skyline Problem.py +++ b/218 The Skyline Problem.py @@ -54,6 +54,8 @@ class Solution: def getSkyline(self, buildings): """ Sweep line + The change of skyline only happens at start and end of buildings. + Treat a building as entering line and leaving line :type buildings: list[list[int]] :rtype: list[list[int]] @@ -65,28 +67,28 @@ def getSkyline(self, buildings): events[left].starts.append(building) # possible multiple building at the same x-coordinate. events[right].ends.append(building) - cur_heap = [] # Heap of buildings currently standing. - cur_max_h = 0 # current max height of standing buildings. + heap_h = [] # Heap of buildings currently standing. + cur_h = 0 # current max height of standing buildings. the current skyline ret = [] # Process events in order by x-coordinate. for x, event in sorted(events.items()): # sort the dictionary by key for building in event.starts: - heapq.heappush(cur_heap, building) + heapq.heappush(heap_h, building) for building in event.ends: building.deleted = True # Pop any finished buildings from the top of the heap. # To avoid using multiset - lazy deletion. - while cur_heap and cur_heap[0].deleted: - heapq.heappop(cur_heap) + while heap_h and heap_h[0].deleted: + heapq.heappop(heap_h) # Top of heap (if any) is the highest standing building, so # its height is the current height of the skyline. - new_h = cur_heap[0].h if cur_heap else 0 + new_h = heap_h[0].h if heap_h else 0 - if new_h != cur_max_h: - cur_max_h = new_h - ret.append([x, cur_max_h]) + if new_h != cur_h: + cur_h = new_h + ret.append([x, cur_h]) return ret From 974b3bb61da38c0e8ade7b78e2c99a3e9e08e54c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 2 Feb 2016 18:32:13 -0800 Subject: [PATCH 141/585] simplify find k-th in union of two sorted array --- 002 Median of Two Sorted Arrays.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/002 Median of Two Sorted Arrays.py b/002 Median of Two Sorted Arrays.py index db560a1..40c858a 100644 --- a/002 Median of Two Sorted Arrays.py +++ b/002 Median of Two Sorted Arrays.py @@ -55,16 +55,13 @@ def find_kth(self, A, B, k): n = len(B) # pay attention to consider the equal sign. Assigning equal sign is an art. - if A[m/2] > B[n/2]: + if A[m/2] >= B[n/2]: if k > m/2+n/2: - return self.find_kth(A, B[n/2+1:], k-n/2-1) # exclude B[n/2] + return self.find_kth(A, B[n/2+1:], k-n/2-1) # exclude B[n/2] to make progress else: - return self.find_kth(A[:m/2], B, k) # exclude A[m/2] + return self.find_kth(A[:m/2], B, k) # exclude A[m/2] to make progess else: - if k > m/2+n/2: - return self.find_kth(A[m/2+1:], B, k-m/2-1) # exclude A[m/2] - else: - return self.find_kth(A, B[:n/2], k) # exclude B[n/2] + return self.find_kth(B, A, k) if __name__ == "__main__": From 7b5461a7c3cd1b19ddb320a5fc761240551cf75a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 2 Feb 2016 18:33:37 -0800 Subject: [PATCH 142/585] simplify find k-th in union of two sorted array --- 002 Median of Two Sorted Arrays.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/002 Median of Two Sorted Arrays.py b/002 Median of Two Sorted Arrays.py index 40c858a..8aebcfa 100644 --- a/002 Median of Two Sorted Arrays.py +++ b/002 Median of Two Sorted Arrays.py @@ -43,23 +43,17 @@ def find_kth(self, A, B, k): :param k: index starting from 0 :return: """ - if not A: - return B[k] - if not B: - return A[k] - - if k == 0: - return min(A[0], B[0]) - - m = len(A) - n = len(B) + if not A: return B[k] + if not B: return A[k] + if k == 0: return min(A[0], B[0]) + m, n = len(A), len(B) # pay attention to consider the equal sign. Assigning equal sign is an art. if A[m/2] >= B[n/2]: if k > m/2+n/2: return self.find_kth(A, B[n/2+1:], k-n/2-1) # exclude B[n/2] to make progress else: - return self.find_kth(A[:m/2], B, k) # exclude A[m/2] to make progess + return self.find_kth(A[:m/2], B, k) # exclude A[m/2] to make progress else: return self.find_kth(B, A, k) From ce4e66d159634b5a084dba2296a73d0295eb6a8c Mon Sep 17 00:00:00 2001 From: Jie Shen <1993sj1993@gmail.com> Date: Wed, 3 Feb 2016 10:50:05 +0800 Subject: [PATCH 143/585] Fix issue #6 Fix the MLE error of problem 106 --- ...ree from Preorder and Inorder Traversal.py | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/106 Construct Binary Tree from Preorder and Inorder Traversal.py b/106 Construct Binary Tree from Preorder and Inorder Traversal.py index aaa4c20..0951029 100644 --- a/106 Construct Binary Tree from Preorder and Inorder Traversal.py +++ b/106 Construct Binary Tree from Preorder and Inorder Traversal.py @@ -15,7 +15,7 @@ def __init__(self, x): class Solution: - def buildTree(self, preorder, inorder): + def buildTree_MLE(self, preorder, inorder): """ Recursive algorithm. Pre-order, in-order, post-order traversal relationship @@ -38,3 +38,24 @@ def buildTree(self, preorder, inorder): root.right = self.buildTree(preorder[root_index+1:], inorder[root_index+1:]) return root + + def buildTree(self, preorder, inorder): + """ + Same idea as the last one, just use integer instead of list + + :type preorder: List[int] + :type inorder: List[int] + :rtype: TreeNode + """ + self.preorder = preorder + self.inorder = inorder + return self._buildTree(0, len(preorder), 0, len(inorder)) + + def _buildTree(self, pre_start, pre_end, in_start, in_end): + if pre_start >= pre_end: + return None + root = TreeNode(self.preorder[pre_start]) + offset = self.inorder[in_start:in_end + 1].index(root.val) + root.left = self._buildTree(pre_start + 1, pre_start + offset + 1, in_start, in_start + offset) + root.right = self._buildTree(pre_start + offset + 1, pre_end, in_start + offset + 1, in_end) + return root From 268662b82da5db30c292dfdb7320798beac3504a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 6 Feb 2016 23:53:18 -0800 Subject: [PATCH 144/585] doc --- 324 Wiggle Sort II.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/324 Wiggle Sort II.py b/324 Wiggle Sort II.py index e3294d3..dac3e27 100644 --- a/324 Wiggle Sort II.py +++ b/324 Wiggle Sort II.py @@ -1,5 +1,5 @@ """ -iven an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3].... +Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3].... Example: (1) Given nums = [1, 5, 1, 1, 6, 4], one possible answer is [1, 4, 1, 5, 1, 6]. From 8d49c6d46d7c563630cf4d494b8cd6e354e9d87a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 7 Aug 2016 16:22:55 -0700 Subject: [PATCH 145/585] power of 4, modular calculation --- 325 Maximum Size Subarray Sum Equals k.py | 2 +- 342 Power of Four.py | 48 +++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 342 Power of Four.py diff --git a/325 Maximum Size Subarray Sum Equals k.py b/325 Maximum Size Subarray Sum Equals k.py index 9a77477..08895ab 100644 --- a/325 Maximum Size Subarray Sum Equals k.py +++ b/325 Maximum Size Subarray Sum Equals k.py @@ -24,4 +24,4 @@ def maxSubArrayLen(self, A, k): if s not in m: m[s] = i - return maxa \ No newline at end of file + return maxa diff --git a/342 Power of Four.py b/342 Power of Four.py new file mode 100644 index 0000000..02eeea9 --- /dev/null +++ b/342 Power of Four.py @@ -0,0 +1,48 @@ +""" +Given an integer (signed 32 bits), write a function to check whether it is a power of 4. + +Example: +Given num = 16, return true. Given num = 5, return false. + +Follow up: Could you solve it without loops/recursion? +""" + + +__author__ = 'Daniel' + + +class Solution(object): + def isPowerOfFour(self, num): + """ + Modular calculation + 4^a mod 3 + = (1)^a mod 3 + = 1 + :param num: + :return: + """ + if num < 1: + return False + if num & num -1 != 0: + return False + + return num % 3 == 1 + + def isPowerOfFourNaive(self, num): + """ + Naive Determine number of 0 bits to be even + :type num: int + :rtype: bool + """ + if num < 1: + return False + if num & num-1 != 0: + return False + + while True: + if num == 0: + return False + elif num == 1: + return True + + num >>= 2 \ No newline at end of file From df1f7aef4af9bc3abfd7b2b1fa0143b79a0d292a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 7 Aug 2016 23:25:00 -0700 Subject: [PATCH 146/585] new --- 338 Counting Bits.py | 49 ++++++++++++++++++++++++++++ 344 Reverse String.py | 13 ++++++++ 345 Reverse Vowels of a String.py | 25 ++++++++++++++ 349 Intersection of Two Arrays.py | 23 +++++++++++++ 350 Intersection of Two Arrays II.py | 44 +++++++++++++++++++++++++ 367 Valid Perfect Square.py | 38 +++++++++++++++++++++ 371 Sum of Two Integers.py | 39 ++++++++++++++++++++++ 374 Guess Number Higher or Lower.py | 45 +++++++++++++++++++++++++ 8 files changed, 276 insertions(+) create mode 100644 338 Counting Bits.py create mode 100644 344 Reverse String.py create mode 100644 345 Reverse Vowels of a String.py create mode 100644 349 Intersection of Two Arrays.py create mode 100644 350 Intersection of Two Arrays II.py create mode 100644 367 Valid Perfect Square.py create mode 100644 371 Sum of Two Integers.py create mode 100644 374 Guess Number Higher or Lower.py diff --git a/338 Counting Bits.py b/338 Counting Bits.py new file mode 100644 index 0000000..dec74d0 --- /dev/null +++ b/338 Counting Bits.py @@ -0,0 +1,49 @@ +""" +Given a non negative integer number num. For every numbers i in the range 0 <= i <= num calculate the number of 1's in +their binary representation and return them as an array. + +Example: +For num = 5 you should return [0,1,1,2,1,2]. + +Follow up: + +It is very easy to come up with a solution with run time O(n*sizeof(integer)). But can you do it in linear time O(n) / +possibly in a single pass? + +Space complexity should be O(n). +Can you do it like a boss? Do it without using any builtin function like __builtin_popcount in c++ or in any other +language. +""" +__author__ = 'Daniel' + + +class Solution(object): + def countBits(self, num): + """ + Dynamic programming: make use of what you have produced already + 0 => 0 + 1 => 1 + + 10 => 1+0 + 11 => 1+1 + + 100 => 1+0 + 101 => 1+1 + 110 => 1+1 + 111 => 1+2 + + :type num: int + :rtype: List[int] + """ + ret = [0] + i = 0 + hi = len(ret) + while len(ret) < num + 1: + if i == hi: + i = 0 + hi = len(ret) + + ret.append(1+ret[i]) + i += 1 + + return ret diff --git a/344 Reverse String.py b/344 Reverse String.py new file mode 100644 index 0000000..ab234f1 --- /dev/null +++ b/344 Reverse String.py @@ -0,0 +1,13 @@ +""" +Write a function that takes a string as input and returns the string reversed. +""" +__author__ = 'Daniel' + + +class Solution(object): + def reverseString(self, s): + """ + :type s: str + :rtype: str + """ + return s[::-1] \ No newline at end of file diff --git a/345 Reverse Vowels of a String.py b/345 Reverse Vowels of a String.py new file mode 100644 index 0000000..aeb55fc --- /dev/null +++ b/345 Reverse Vowels of a String.py @@ -0,0 +1,25 @@ +""" +Write a function that takes a string as input and reverse only the vowels of a string. +""" +__author__ = 'Daniel' + + +class Solution(object): + def reverseVowels(self, s): + """ + :type s: str + :rtype: str + """ + vowels = set(['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']) + s = list(s) + j = len(s) - 1 + i = 0 + while i < j: + if s[i] in vowels: + while s[j] not in vowels: j -= 1 + s[i], s[j] = s[j], s[i] + j -= 1 + + i += 1 + + return "".join(s) \ No newline at end of file diff --git a/349 Intersection of Two Arrays.py b/349 Intersection of Two Arrays.py new file mode 100644 index 0000000..d6ab03a --- /dev/null +++ b/349 Intersection of Two Arrays.py @@ -0,0 +1,23 @@ +""" +Given two arrays, write a function to compute their intersection. + +Example: +Given nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2]. + +Note: +Each element in the result must be unique. +The result can be in any order. +""" + +__author__ = 'Daniel' + + +class Solution(object): + def intersection(self, nums1, nums2): + """ + O(n+m) + :type nums1: List[int] + :type nums2: List[int] + :rtype: List[int] + """ + return list(set(nums1).intersection(set(nums2))) \ No newline at end of file diff --git a/350 Intersection of Two Arrays II.py b/350 Intersection of Two Arrays II.py new file mode 100644 index 0000000..ac9d779 --- /dev/null +++ b/350 Intersection of Two Arrays II.py @@ -0,0 +1,44 @@ +""" +Given two arrays, write a function to compute their intersection. + +Example: +Given nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2, 2]. + +Note: +Each element in the result should appear as many times as it shows in both arrays. +The result can be in any order. +Follow up: +What if the given array is already sorted? How would you optimize your algorithm? +What if nums1's size is small compared to nums2's size? Which algorithm is better? +What if elements of nums2 are stored on disk, and the memory is limited such that you cannot load all elements into the +memory at once? +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class Solution(object): + def intersect(self, nums1, nums2): + """ + Hash table + Time O(m+n) + Memory O(m+n) + :type nums1: List[int] + :type nums2: List[int] + :rtype: List[int] + """ + h1, h2 = defaultdict(int), defaultdict(int) + for a in nums1: + h1[a] += 1 + for b in nums2: + h2[b] += 1 + + ret = [] + for k, v in h1.items(): + cnt = min(v, h2[k]) + ret.extend([k]*cnt) + + return ret + + diff --git a/367 Valid Perfect Square.py b/367 Valid Perfect Square.py new file mode 100644 index 0000000..ae71a6a --- /dev/null +++ b/367 Valid Perfect Square.py @@ -0,0 +1,38 @@ +""" +Given a positive integer num, write a function which returns True if num is a perfect square else False. + +Note: Do not use any built-in library function such as sqrt. + +Example 1: + +Input: 16 +Returns: True +Example 2: + +Input: 14 +Returns: False +""" +__author__ = 'Daniel' + + +class Solution(object): + def isPerfectSquare(self, num): + """ + Debugging binary search + :type num: int + :rtype: bool + """ + if num == 1: return True + lo = 1 + hi = num/2 + 1 + while lo < hi: + mid = (lo + hi) / 2 + midsq = mid**2 + if midsq == num: + return True + elif midsq < num: + lo = mid + 1 + else: + hi = mid + + return False diff --git a/371 Sum of Two Integers.py b/371 Sum of Two Integers.py new file mode 100644 index 0000000..4fac485 --- /dev/null +++ b/371 Sum of Two Integers.py @@ -0,0 +1,39 @@ +""" +Calculate the sum of two integers a and b, but you are not allowed to use the operator + and -. + +Example: +Given a = 1 and b = 2, return 3. +""" + +__author__ = 'Daniel' + + +class Solution(object): + def getSum(self, a, b): + """ + circuit full-adder + since Python don't restrict to 32bit, we need + 1. Masking + 2. Overflow handling + :type a: int + :type b: int + :rtype: int + """ + MAX = 0x7FFFFFFF + MSK = 0xFFFFFFFF + + carry = (a & b) << 1 + out = a ^ b + + # convert to 32 bit + carry &= MSK + out &= MSK + + if carry != 0: + return self.getSum(out, carry) + else: + # handle overflow + if out < MAX: + return out + else: # negative + return ~(out ^ MSK) \ No newline at end of file diff --git a/374 Guess Number Higher or Lower.py b/374 Guess Number Higher or Lower.py new file mode 100644 index 0000000..9d1588b --- /dev/null +++ b/374 Guess Number Higher or Lower.py @@ -0,0 +1,45 @@ +""" +We are playing the Guess Game. The game is as follows: + +I pick a number from 1 to n. You have to guess which number I picked. + +Every time you guess wrong, I'll tell you whether the number is higher or lower. + +You call a pre-defined API guess(int num) which returns 3 possible results (-1, 1, or 0): + +-1 : My number is lower + 1 : My number is higher + 0 : Congrats! You got it! +Example: +n = 10, I pick 6. + +Return 6. +""" +__author__ = 'Daniel' + + +# The guess API is already defined for you. +# @param num, your guess +# @return -1 if my number is lower, 1 if my number is higher, otherwise return 0 + +def guess(num): + return -1 + + +class Solution(object): + def guessNumber(self, n): + """ + Binary search, transform [1, n] into [1, n+1), to make sure the loop is making progress + :type n: int + :rtype: int + """ + lo, hi = 1, n+1 + while True: + mid = (lo + hi) / 2 + g = guess(mid) + if g == 0: + return mid + elif g < 1: + hi = mid + else: + lo = mid + 1 From 914754f30af677523fdca4028d65e888305ca957 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 8 Aug 2016 00:05:39 -0700 Subject: [PATCH 147/585] number dp --- 343 Integer Break.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 343 Integer Break.py diff --git a/343 Integer Break.py b/343 Integer Break.py new file mode 100644 index 0000000..d8339f9 --- /dev/null +++ b/343 Integer Break.py @@ -0,0 +1,37 @@ +""" +Given a positive integer n, break it into the sum of at least two positive integers and maximize the product of those +integers. Return the maximum product you can get. + +For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4). + +Note: You may assume that n is not less than 2 and not larger than 58. +""" +__author__ = 'Daniel' + + +class Solution(object): + def integerBreak(self, n): + """ + First visualize the breakdown process into a search tree. The search tree dynamic programming + Dynamic programming + Let F[i] be the max product of summation breakdown integers of number i + For each operands of the plus sign, there are choices of break it or not; thus we have for cases + F[i] = max( + F[j] * F[i-j], + j * F[i-j], + F[j] * (i-j), + j * (i-j), + for j in [1, i) + ) if break down i + :type n: int + :rtype: int + """ + F = [None for _ in xrange(n+1)] + F[1] = 1 + for i in xrange(2, n+1): + F[i] = max( + max(F[j] * F[i-j], j * F[i-j], F[j] * (i-j), j * (i-j)) + for j in xrange(1, i/2) + ) + + return F[n] From 22cbd24235ca605e876e108ad6c70fcde35474bc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 8 Aug 2016 23:43:43 -0700 Subject: [PATCH 148/585] dp --- 375 Guess Number Higher or Lower II.py | 83 ++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 375 Guess Number Higher or Lower II.py diff --git a/375 Guess Number Higher or Lower II.py b/375 Guess Number Higher or Lower II.py new file mode 100644 index 0000000..d250963 --- /dev/null +++ b/375 Guess Number Higher or Lower II.py @@ -0,0 +1,83 @@ +""" +We are playing the Guess Game. The game is as follows: + +I pick a number from 1 to n. You have to guess which number I picked. + +Every time you guess wrong, I'll tell you whether the number I picked is higher or lower. + +However, when you guess a particular number x, and you guess wrong, you pay $x. You win the game when you guess the +number I picked. + +Example: + +n = 10, I pick 8. + +First round: You guess 5, I tell you that it's higher. You pay $5. +Second round: You guess 7, I tell you that it's higher. You pay $7. +Third round: You guess 9, I tell you that it's lower. You pay $9. + +Game over. 8 is the number I picked. + +You end up paying $5 + $7 + $9 = $21. +Given a particular n >= 1, find out how much money you need to have to guarantee a win. +""" +__author__ = 'Daniel' + + +class Solution(object): + def getMoneyAmount(self, n): + """ + Let F[i][j] be the min cost of guessing [i, j) + F[i][j] = min( + k + max(F[i][k], F[k+1][j]) for k in [i, j) + ) + Draw a matrix to show the population direction + [ ] -> [ ] + ^ + | + [ ] + O(n^3) + + Edge cases: + F[i][i+1] = 0 + :type n: int + :rtype: int + """ + N = n + 1 # guessing [1, N), where N = n + 1 + F = [[0 for _ in xrange(N+1)] for _ in xrange(N+1)] + for i in xrange(n, 0, -1): + for j in xrange(i+2, N+1): + F[i][j] = min( + k + max(F[i][k], F[k+1][j]) + for k in xrange(i, j) + ) + + return F[1][N] + + def getMoneyAmountError(self, n): + """ + Cost for number. Guarantee a win. + Let C[i] be the min requirement of the number of wrong guesses + Let F[i] be the min requirement of money + + C[i] = min(C[k-1] + 1 + C[i-k] for k in [1, i]) + F[i] = min(F[k-1] + k + k*C[i-k] + F[i-k] for k in [1, i]) + O(n^2) + + Still one-directional guess + Error: F[i] does not correspond to C[i] + :type n: int + :rtype: int + """ + C = [0 for _ in xrange(n+1)] + F = [0 for _ in xrange(n+1)] + for i in xrange(2, n+1): + C[i] = min(1 + max(C[k-1], C[i-k]) for k in xrange(1, i+1)) + F[i] = min(k + max(F[k-1], k*C[i-k] + F[i-k]) for k in xrange(1, i+1)) + + return F[n] + + +if __name__ == "__main__": + print Solution().getMoneyAmount(100) + From cf868a809a722ab65220ba6200fdb37f68158c24 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 9 Aug 2016 23:56:12 -0700 Subject: [PATCH 149/585] wiggle n^2 --- 376 Wiggle Subsequence.py | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 376 Wiggle Subsequence.py diff --git a/376 Wiggle Subsequence.py b/376 Wiggle Subsequence.py new file mode 100644 index 0000000..4512c6c --- /dev/null +++ b/376 Wiggle Subsequence.py @@ -0,0 +1,42 @@ +""" +A sequence of numbers is called a wiggle sequence if the differences between successive numbers strictly alternate +between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with +fewer than two elements is trivially a wiggle sequence. + +For example, [1,7,4,9,2,5] is a wiggle sequence because the differences (6,-3,5,-7,3) are alternately positive and +negative. In contrast, [1,4,7,2,5] and [1,7,4,5,5] are not wiggle sequences, the first because its first two +differences are positive and the second because its last difference is zero. + +Given a sequence of integers, return the length of the longest subsequence that is a wiggle sequence. A subsequence is +obtained by deleting some number of elements (eventually, also zero) from the original sequence, leaving the remaining +elements in their original order. +""" +__author__ = 'Daniel' + + +class Solution(object): + def wiggleMaxLength(self, A): + """ + Let H[i] be wiggle length ends at i, with A[i] as high point + Let L[i] be similarly defined but as low point. + O(n^2) + :type A: List[int] + :rtype: int + """ + if not A: + return 0 + + N = len(A) + H = [1 for _ in xrange(N)] + L = [1 for _ in xrange(N)] + gmax = 1 + for i in xrange(1, N): + for j in xrange(i): + if A[i] > A[j]: + H[i] = max(H[i], L[j] + 1) + elif A[i] < A[j]: + L[i] = max(L[i], H[j] + 1) + + gmax = max(gmax, H[i], L[i]) + + return gmax From a4765b20ab7a3bd9a101e7e82ffb3b346c686d4b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 10 Aug 2016 00:45:53 -0700 Subject: [PATCH 150/585] wiggle O(n) --- 376 Wiggle Subsequence.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/376 Wiggle Subsequence.py b/376 Wiggle Subsequence.py index 4512c6c..6b09f3d 100644 --- a/376 Wiggle Subsequence.py +++ b/376 Wiggle Subsequence.py @@ -16,6 +16,40 @@ class Solution(object): def wiggleMaxLength(self, A): + """ + Let H[i] be max wiggle length for [0, i] with A[i] as high point + Let L[i] be similarly defined but as low point. + + Consider A[i] > A[i-1]: + H[i] = L[i-1] + 1 # wiggle up + L[i] = L[i-1] # + A[i] < A[i-1] case has similar formula + + H[i] = H[i-1] + = L[i-1] + 1 + + L[i] = L[i-1] + = H[i-1] + 1 + + Therefore, max(H[i], L[i]) are monotonously non-decreasing (rather than H[i] or L[i] monotonously + non-decreasing separately. + O(n) + + Additionally, possibly space optimized to O(1) by reusing space + :type A: List[int] + :rtype: int + """ + if not A: return 0 + N = len(A) + H = [1 for _ in xrange(N)] + L = [1 for _ in xrange(N)] + for i in xrange(1, N): + L[i] = H[i-1] + 1 if A[i] < A[i-1] else L[i-1] + H[i] = L[i-1] + 1 if A[i] > A[i-1] else H[i-1] + + return max(H[N-1], L[N-1]) + + def wiggleMaxLengthSuboptimal(self, A): """ Let H[i] be wiggle length ends at i, with A[i] as high point Let L[i] be similarly defined but as low point. @@ -23,8 +57,7 @@ def wiggleMaxLength(self, A): :type A: List[int] :rtype: int """ - if not A: - return 0 + if not A: return 0 N = len(A) H = [1 for _ in xrange(N)] From cd37ade959389ed80b3c23962e6a683481eddea7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 28 Aug 2016 17:34:38 -0700 Subject: [PATCH 151/585] easy --- 383 Ransom Note.py | 36 +++++++++++++++++++ 387 First Unique Character in a String.py | 39 +++++++++++++++++++++ 389 Find the Difference.py | 42 +++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 383 Ransom Note.py create mode 100644 387 First Unique Character in a String.py create mode 100644 389 Find the Difference.py diff --git a/383 Ransom Note.py b/383 Ransom Note.py new file mode 100644 index 0000000..2af1daa --- /dev/null +++ b/383 Ransom Note.py @@ -0,0 +1,36 @@ +""" +Given an arbitrary ransom note string and another string containing letters from all the magazines, write a function +that will return true if the ransom note can be constructed from the magazines; otherwise, it will return false. + +Each letter in the magazine string can only be used once in your ransom note. + +Note: +You may assume that both strings contain only lowercase letters. + +canConstruct("a", "b") -> false +canConstruct("aa", "ab") -> false +canConstruct("aa", "aab") -> true +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class Solution(object): + def canConstruct(self, ransomNote, magazine): + """ + :type ransomNote: str + :type magazine: str + :rtype: bool + """ + d = defaultdict(int) + + for e in magazine: + d[e] += 1 + + for e in ransomNote: + if d[e] == 0: + return False + d[e] -= 1 + + return True \ No newline at end of file diff --git a/387 First Unique Character in a String.py b/387 First Unique Character in a String.py new file mode 100644 index 0000000..35b18ac --- /dev/null +++ b/387 First Unique Character in a String.py @@ -0,0 +1,39 @@ +""" +Given a string, find the first non-repeating character in it and return it's index. If it doesn't exist, return -1. + +Examples: + +s = "leetcode" +return 0. + +s = "loveleetcode", +return 2. +Note: You may assume the string contain only lowercase letters. + + +""" +__author__ = 'Daniel' + + +class Solution(object): + def firstUniqChar(self, s): + """ + :type s: str + :rtype: int + """ + if not s: + return -1 + + first = {} + for i, v in enumerate(list(s)): + if v not in first: + first[v] = i + else: + first[v] = -1 + + lst = filter(lambda x: x != -1, first.values()) + return min(lst) if lst else -1 + + +if __name__ == "__main__": + assert Solution().firstUniqChar("leetcode") == 0 \ No newline at end of file diff --git a/389 Find the Difference.py b/389 Find the Difference.py new file mode 100644 index 0000000..a06cd21 --- /dev/null +++ b/389 Find the Difference.py @@ -0,0 +1,42 @@ +""" +Given two strings s and t which consist of only lowercase letters. + +String t is generated by random shuffling string s and then add one more letter at a random position. + +Find the letter that was added in t. + +Example: + +Input: +s = "abcd" +t = "abcde" + +Output: +e + +Explanation: +'e' is the letter that was added. + +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class Solution(object): + def findTheDifference(self, s, t): + """ + :type s: str + :type t: str + :rtype: str + """ + d = defaultdict(int) + for e in s: + d[e] += 1 + + for e in t: + if d[e] == 0: + return e + d[e] -= 1 + + return '' \ No newline at end of file From f7bb5003bb266aa519d11f3efc53a724bdcf2d44 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 28 Aug 2016 23:18:06 -0700 Subject: [PATCH 152/585] triplet --- 334 Increasing Triplet Subsequence.py | 55 ++++++++++ 341 Flatten Nested List Iterator.py | 152 ++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 334 Increasing Triplet Subsequence.py create mode 100644 341 Flatten Nested List Iterator.py diff --git a/334 Increasing Triplet Subsequence.py b/334 Increasing Triplet Subsequence.py new file mode 100644 index 0000000..2a26933 --- /dev/null +++ b/334 Increasing Triplet Subsequence.py @@ -0,0 +1,55 @@ +""" +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. +Your algorithm should run in O(n) time complexity and O(1) space complexity. + +Examples: +Given [1, 2, 3, 4, 5], +return true. + +Given [5, 4, 3, 2, 1], +return false. +""" +import sys + +__author__ = 'Daniel' + + +class Solution(object): + def increasingTriplet(self, nums): + """ + Brute force: O(N^3) + :type nums: List[int] + :rtype: bool + """ + min1 = sys.maxint + min2 = sys.maxint + for e in nums: + if e < min1: + min1 = e + elif e != min1 and e < min2: + min2 = e + elif e > min2: + return True + + return False + + def increasingTripletError(self, nums): + """ + use stack + :type nums: List[int] + :rtype: bool + """ + stk = [] + for elt in nums: + while stk and stk[-1] >= elt: + stk.pop() + + stk.append(elt) + if len(stk) >= 3: + return True + + return False diff --git a/341 Flatten Nested List Iterator.py b/341 Flatten Nested List Iterator.py new file mode 100644 index 0000000..3866387 --- /dev/null +++ b/341 Flatten Nested List Iterator.py @@ -0,0 +1,152 @@ +""" +Given a nested list of integers, implement an iterator to flatten it. + +Each element is either an integer, or a list -- whose elements may also be integers or other lists. + +Example 1: +Given the list [[1,1],2,[1,1]], + +By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,1,2,1,1]. + +Example 2: +Given the list [1,[4,[6]]], + +By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,4,6]. +""" +__author__ = 'Daniel' + + +""" +This is the interface that allows for creating nested lists. +You should not implement it, or speculate about its implementation +""" + + +class NestedInteger(object): + def isInteger(self): + """ + @return True if this NestedInteger holds a single integer, rather than a nested list. + :rtype bool + """ + return True + + def getInteger(self): + """ + @return the single integer that this NestedInteger holds, if it holds a single integer + Return None if this NestedInteger holds a nested list + :rtype int + """ + return 0 + + def getList(self): + """ + @return the nested list that this NestedInteger holds, if it holds a nested list + Return None if this NestedInteger holds a single integer + :rtype List[NestedInteger] + """ + return [] + + +class NestedIterator(object): + def __init__(self, nestedList): + """ + Initialize your data structure here. + :type nestedList: List[NestedInteger] + + 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() + + Possible to compile nl and idx into a tuple. + """ + self.stk = [[nestedList, 0]] + + 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 + + def hasNext(self): + """ + Put the pointer movement logic in the hasNext() + :rtype: bool + """ + while self.stk: + nl, idx = self.stk[-1] + if idx < len(nl): + ni = nl[idx] + if ni.isInteger(): + return True + else: + self.stk[-1][1] = idx + 1 + nxt_nl = ni.getList() + self.stk.append([nxt_nl, 0]) + else: + self.stk.pop() + + return False + + + +class NestedIteratorVerbose(object): + def __init__(self, nestedList): + """ + Initialize your data structure here. + :type nestedList: List[NestedInteger] + + 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() + + Possible to compile nl and idx into a tuple. + """ + self.nl_stk = [nestedList] + self.idx_stk = [0] + + def next(self): + """ + :rtype: int + """ + if self.hasNext(): + nl = self.nl_stk[-1] + idx = self.idx_stk[-1] + nxt = nl[idx] + self.idx_stk[-1] = idx + 1 + return nxt + + raise StopIteration() + + def hasNext(self): + """ + Put the pointer movement logic in the hasNext() + :rtype: bool + """ + while self.nl_stk: + nl = self.nl_stk[-1] + idx = self.idx_stk[-1] + if idx < len(nl): + ni = nl[idx] + if ni.isInteger(): + return True + else: + self.idx_stk[-1] = idx+1 + nxt_nl = ni.getList() + nxt_idx = 0 + self.nl_stk.append(nxt_nl) + self.idx_stk.append(nxt_idx) + else: + self.nl_stk.pop() + self.idx_stk.pop() + + return False + + +# 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 From 31d26470814d950f92ab1790341897a388d0ed24 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 29 Aug 2016 00:07:50 -0700 Subject: [PATCH 153/585] Fisher-Yates shuffle --- 384 Shuffle an Array.py | 58 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 384 Shuffle an Array.py diff --git a/384 Shuffle an Array.py b/384 Shuffle an Array.py new file mode 100644 index 0000000..f38d5dd --- /dev/null +++ b/384 Shuffle an Array.py @@ -0,0 +1,58 @@ +""" +Shuffle a set of numbers without duplicates. + +Example: + +// Init an array with set 1, 2, and 3. +int[] nums = {1,2,3}; +Solution solution = new Solution(nums); + +// Shuffle the array [1,2,3] and return its result. Any permutation of [1,2,3] must equally likely to be returned. +solution.shuffle(); + +// Resets the array back to its original configuration [1,2,3]. +solution.reset(); + +// Returns the random shuffling of array [1,2,3]. +solution.shuffle(); +""" +import random + +__author__ = 'Daniel' + + +class Solution(object): + def __init__(self, nums): + """ + :type nums: List[int] + :type size: int + """ + self.original = nums + + def reset(self): + """ + Resets the array to its original configuration and return it. + :rtype: List[int] + """ + return list(self.original) + + def shuffle(self): + """ + Returns a random shuffling of the array. + like shuffle the poker cards + in-place shuffling and avoid dynamic resizing the list + :rtype: List[int] + """ + lst = self.reset() + n = len(lst) + for i in xrange(n): + j = random.randrange(i, n) + lst[i], lst[j] = lst[j], lst[i] + + return lst + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(nums) +# param_1 = obj.reset() +# param_2 = obj.shuffle() \ No newline at end of file From 67ade862645f3d9f5707511e3144432bae5d1dc6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 29 Aug 2016 00:08:13 -0700 Subject: [PATCH 154/585] reservoir sample --- 382 Linked List Random Node.py | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 382 Linked List Random Node.py diff --git a/382 Linked List Random Node.py b/382 Linked List Random Node.py new file mode 100644 index 0000000..f7e7ada --- /dev/null +++ b/382 Linked List Random Node.py @@ -0,0 +1,61 @@ +""" +Given a singly linked list, return a random node's value from the linked list. Each node must have the same probability +of being chosen. + +Follow up: +What if the linked list is extremely large and its length is unknown to you? Could you solve this efficiently without +using extra space? + +Example: + +// Init a singly linked list [1,2,3]. +ListNode head = new ListNode(1); +head.next = new ListNode(2); +head.next.next = new ListNode(3); +Solution solution = new Solution(head); + +// getRandom() should return either 1, 2, or 3 randomly. Each element should have equal probability of returning. +solution.getRandom(); +""" +import random + +__author__ = 'Daniel' + + +# Definition for singly-linked list. +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + + +class Solution(object): + def __init__(self, head): + """ + Reservoir sampling + :param: head The linked list's head. + Note that the head is guaranteed to be not null, so it contains at least one node. + :type head: ListNode + """ + self.head = head + + def getRandom(self): + """ + Returns a random node's value. + :rtype: int + """ + ret = self.head + cur = self.head.next + idx = 1 + while cur: + if random.randrange(0, idx+1) == idx: + ret = cur + cur = cur.next + idx += 1 + + return ret.val + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(head) +# param_1 = obj.getRandom() \ No newline at end of file From 260dc5e765c9112ddf921ac8e7d5e64ccaabd42a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 29 Aug 2016 23:35:23 -0700 Subject: [PATCH 155/585] lexical order --- 386 Lexicographical Numbers.py | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 386 Lexicographical Numbers.py diff --git a/386 Lexicographical Numbers.py b/386 Lexicographical Numbers.py new file mode 100644 index 0000000..7336e7d --- /dev/null +++ b/386 Lexicographical Numbers.py @@ -0,0 +1,51 @@ +""" +Given an integer n, return 1 - n in lexicographical order. + +For example, given 13, return: [1,10,11,12,13,2,3,4,5,6,7,8,9]. + +Please optimize your algorithm to use less time and space. The input size may be as large as 5,000,000. +""" +__author__ = 'Daniel' + + +class Solution(object): + def lexicalOrder(self, n): + """ + :type n: int + :rtype: List[int] + """ + def gen(): + i = 1 + for _ in xrange(n): + yield i + if i * 10 <= n: + i *= 10 # * 10 + elif i % 10 != 9 and i + 1 <= n: + i += 1 # for current digit + else: + i /= 10 # move to next digit + while i % 10 == 9: + i /= 10 + i += 1 + + return list(gen()) + + def lexicalOrderError(self, n): + """ + :type n: int + :rtype: List[int] + """ + ret = [] + for i in xrange(1, 10): + sig = 1 + while i * sig <= n: + ret.extend(range( + i * sig, + min((1+i)*sig-1, n)+1), + ) + sig *= 10 + + return ret + +if __name__ == "__main__": + print Solution().lexicalOrder(100) \ No newline at end of file From 0699d65768a09beca7a6083cc2b5adbfe1cbd02a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 30 Aug 2016 23:02:50 -0700 Subject: [PATCH 156/585] digit combinatorics 998 --- 357 Count Numbers with Unique Digits.py | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 357 Count Numbers with Unique Digits.py diff --git a/357 Count Numbers with Unique Digits.py b/357 Count Numbers with Unique Digits.py new file mode 100644 index 0000000..e6078d4 --- /dev/null +++ b/357 Count Numbers with Unique Digits.py @@ -0,0 +1,30 @@ +""" +Given a non-negative integer n, count all numbers with unique digits, x, where 0 <= x < 10^n. + +Example: +Given n = 2, return 91. (The answer should be the total numbers in the range of 0 <= x < 100, excluding +[11,22,33,44,55,66,77,88,99]) +""" +__author__ = 'Daniel' + + +class Solution(object): + def countNumbersWithUniqueDigits(self, n): + """ + Let F(i) be the number of numbers with unique digits of length i + F(1) = 1 // special case + F(i) = (10-1) * 9 * 8 * (10-i+1) // general case, 998 + (10-1) since the leading digit cannot be zero + + return sum F(i) + :type n: int + :rtype: int + """ + ret = 1 + Fi = 1 + for i in xrange(1, n+1): + Fi *= (10-i+1) + if i == 1: Fi -= 1 + ret += Fi + + return ret From 52a725dfe0b80c1b70bf69db80921924f36dccdd Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 31 Aug 2016 00:29:13 -0700 Subject: [PATCH 157/585] heap --- 347. Top K Frequent Elements.py | 52 +++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 347. Top K Frequent Elements.py diff --git a/347. Top K Frequent Elements.py b/347. Top K Frequent Elements.py new file mode 100644 index 0000000..b8d15ba --- /dev/null +++ b/347. Top K Frequent Elements.py @@ -0,0 +1,52 @@ +""" +Given a non-empty array of integers, return the k most frequent elements. + +For example, +Given [1,1,1,2,2,3] and k = 2, return [1,2]. + +Note: +You may assume k is always valid, 1 <= k <= number of unique elements. +Your algorithm's time complexity must be better than O(n log n), where n is the array's size. +""" +from collections import defaultdict +import heapq + +__author__ = 'Daniel' + + +class Counter(object): + def __init__(self, val, cnt): + self.val = val + self.cnt = cnt + + def __cmp__(self, other): + return self.cnt - other.cnt + + +class Solution(object): + def topKFrequent(self, nums, K): + """ + Count and Maintain a heap with size k -> O(n lg k) + Since python heapq does not support cmp, need to wrap data in a struct + Need to use min heap instead of max heap, since we need to pop the minimal one + :type nums: List[int] + :type K: int + :rtype: List[int] + """ + cnt = defaultdict(int) + for e in nums: + cnt[e] += 1 + + lst = [] + for k, v in cnt.items(): + lst.append(Counter(k, v)) + + ret = [] + for elt in lst: + if len(ret) < K: + heapq.heappush(ret, elt) + else: + heapq.heappushpop(ret, elt) + + return map(lambda x: x.val, ret) + From 95889f81986630208c3b20df50a9d9f41e98ff24 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 1 Sep 2016 20:42:11 -0700 Subject: [PATCH 158/585] house robber --- 213 House Robber II.py | 12 ++--- 337 House Robber III.py | 107 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 337 House Robber III.py diff --git a/213 House Robber II.py b/213 House Robber II.py index e58dcfb..599c601 100644 --- a/213 House Robber II.py +++ b/213 House Robber II.py @@ -26,15 +26,15 @@ def rob(self, nums): return sum(nums) # include first but exclude last - dp = [0 for _ in xrange(n-1+2)] + F = [0 for _ in xrange(n-1+2)] for i in xrange(2, n+1): - dp[i] = max(dp[i-1], dp[i-2]+nums[i-2]) - ret = dp[-1] + F[i] = max(F[i-1], F[i-2]+nums[i-2]) + ret = F[-1] # exclude first but include last - dp = [0 for _ in xrange(n-1+2)] + F = [0 for _ in xrange(n-1+2)] for i in xrange(2, n+1): - dp[i] = max(dp[i-1], dp[i-2]+nums[i-1]) + F[i] = max(F[i-1], F[i-2]+nums[i-1]) - ret = max(ret, dp[-1]) + ret = max(ret, F[-1]) return ret diff --git a/337 House Robber III.py b/337 House Robber III.py new file mode 100644 index 0000000..93d4b26 --- /dev/null +++ b/337 House Robber III.py @@ -0,0 +1,107 @@ +""" +The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the +"root." Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that +"all houses in this place forms a binary tree". It will automatically contact the police if two directly-linked houses +were broken into on the same night. + +Determine the maximum amount of money the thief can rob tonight without alerting the police. + +Example 1: + 3 + / \ + 2 3 + \ \ + 3 1 +Maximum amount of money the thief can rob = 3 + 3 + 1 = 7. +Example 2: + 3 + / \ + 4 5 + / \ \ + 1 3 1 +Maximum amount of money the thief can rob = 4 + 5 = 9. +""" +__author__ = 'Daniel' + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def __init__(self): + self.cache_rob = {} + self.cache_notrob = {} + + def rob(self, root): + """ + possible rob at root + :type root: TreeNode + :rtype: int + """ + if root is None: + return 0 + + if root not in self.cache_rob: + val = max( + self.notrob(root), + root.val + self.notrob(root.left) + self.notrob(root.right) + ) + self.cache_rob[root] = val + + return self.cache_rob[root] + + def notrob(self, root): + """ + not rob at the root + :param root: TreeNode + :return: int + """ + if root is None: + return 0 + + if root not in self.cache_notrob: + val = ( + self.rob(root.left) + + self.rob(root.right) + ) + + self.cache_notrob[root] = val + + return self.cache_notrob[root] + + +class SolutionTLE(object): + def rob(self, root): + """ + :type root: TreeNode + :rtype: int + """ + if root is None: + return 0 + + return max( + self.dorob(root), + self.notrob(root) + ) + + def dorob(self, root): + if root is None: + return 0 + + return ( + root.val + + self.notrob(root.left) + + self.notrob(root.right) + ) + + def notrob(self, root): + if root is None: + return 0 + + return (max(self.notrob(root.left), self.rob(root.left)) + + max(self.notrob(root.right), self.rob(root.right))) From 2027d79a5bd7081be178e9b467f89aac016f063d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 2 Sep 2016 00:10:12 -0700 Subject: [PATCH 159/585] Linked List --- 328 Odd Even Linked List.py | 81 +++++++++++++++++++++++++++++++++++++ 337 House Robber III.py | 2 +- 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 328 Odd Even Linked List.py diff --git a/328 Odd Even Linked List.py b/328 Odd Even Linked List.py new file mode 100644 index 0000000..91c6986 --- /dev/null +++ b/328 Odd Even Linked List.py @@ -0,0 +1,81 @@ +""" +Given a singly linked list, group all odd nodes together followed by the even nodes. Please note here we are talking +about the node number and not the value in the nodes. + +You should try to do it in place. The program should run in O(1) space complexity and O(nodes) time complexity. + +Example: +Given 1->2->3->4->5->NULL, +return 1->3->5->2->4->NULL. + +Note: +The relative order inside both the even and odd groups should remain as it was in the input. +The first node is considered odd, the second node even and so on ... +""" +__author__ = 'Daniel' + + +# Definition for singly-linked list. +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + + +class Solution(object): + def oddEvenList(self, head): + """ + :type head: ListNode + :rtype: ListNode + """ + if not head: + return + + ptr = head # end of odd position + pre = head # don't move the first + cnt = 1 + while pre and pre.next: + cur = pre.next + cnt += 1 + if cnt % 2 == 0: + pre = pre.next + else: + start = ptr.next + nxt = cur.next + + ptr.next = cur + cur.next = start + pre.next = nxt + + ptr = ptr.next + + return head + + def oddEvenListError(self, head): + """ + Wrongly move by node value + :type head: ListNode + :rtype: ListNode + """ + if not head: + return + + ptr = head # end of first parity + parity = ptr.val % 2 + + pre = head + while pre and pre.next: + cur = pre.next + if cur.val % 2 != parity: + pre = pre.next + else: + start = ptr.next + nxt = cur.next + + ptr.next = cur + cur.next = start + pre.next = nxt + + ptr = ptr.next + + return head diff --git a/337 House Robber III.py b/337 House Robber III.py index 93d4b26..a974de4 100644 --- a/337 House Robber III.py +++ b/337 House Robber III.py @@ -57,7 +57,7 @@ def rob(self, root): def notrob(self, root): """ - not rob at the root + not rob at the root :param root: TreeNode :return: int """ From c49e5c6ac4f798e7d7c2f12cb90edbbb2a5b48c0 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 2 Sep 2016 00:28:52 -0700 Subject: [PATCH 160/585] Serialization of a Binary Tree --- ...Preorder Serialization of a Binary Tree.py | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 331 Verify Preorder Serialization of a Binary Tree.py diff --git a/331 Verify Preorder Serialization of a Binary Tree.py b/331 Verify Preorder Serialization of a Binary Tree.py new file mode 100644 index 0000000..f2fae51 --- /dev/null +++ b/331 Verify Preorder Serialization of a Binary Tree.py @@ -0,0 +1,89 @@ +""" +One way to serialize a binary tree is to use pre-order traversal. When we encounter a non-null node, we record the +node's value. If it is a null node, we record using a sentinel value such as #. + + _9_ + / \ + 3 2 + / \ / \ + 4 1 # 6 +/ \ / \ / \ +# # # # # # +For example, the above binary tree can be serialized to the string "9,3,4,#,#,1,#,#,2,#,6,#,#", where # represents a +null node. + +Given a string of comma separated values, verify whether it is a correct preorder traversal serialization of a binary +tree. Find an algorithm without reconstructing the tree. + +Each comma separated value in the string must be either an integer or a character '#' representing null pointer. + +You may assume that the input format is always valid, for example it could never contain two consecutive commas such as +"1,,3". + +Example 1: +"9,3,4,#,#,1,#,#,2,#,6,#,#" +Return true + +Example 2: +"1,#" +Return false + +Example 3: +"9,#,#,1" +Return false +""" +__author__ = 'Daniel' + + +class Solution(object): + def isValidSerialization(self, preorder): + """ + :type preorder: str + :rtype: bool + """ + stk = preorder.split(',') + child_cnt = 0 + while stk: + if stk[-1] == '#': + stk.pop() + child_cnt += 1 + else: + child_cnt -= 2 + if child_cnt < 0: + return False + + stk.pop() + child_cnt += 1 + + return not stk and child_cnt == 1 + + def isValidSerializationSpace(self, preorder): + """ + :type preorder: str + :rtype: bool + """ + stk = preorder.split(',') + child_stk = [] + while stk: + if stk[-1] == '#': + child_stk.append(stk.pop()) # a counter is enough + else: + try: + child_stk.pop() + child_stk.pop() + stk.pop() + child_stk.append('#') + except IndexError: + return False + + return not stk and len(child_stk) == 1 + + +if __name__ == "__main__": + Solution().isValidSerialization("9,3,4,#,#,1,#,#,2,#,6,#,#") + + + + + + From ab04e3fad823252bc8f5e075923468cc2a4e04ee Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 2 Sep 2016 22:43:50 -0700 Subject: [PATCH 161/585] euler path --- 332 Reconstruct Itinerary.py | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 332 Reconstruct Itinerary.py diff --git a/332 Reconstruct Itinerary.py b/332 Reconstruct Itinerary.py new file mode 100644 index 0000000..85caa7a --- /dev/null +++ b/332 Reconstruct Itinerary.py @@ -0,0 +1,55 @@ +""" +Given a list of airline tickets represented by pairs of departure and arrival airports [from, to], reconstruct the +itinerary in order. All of the tickets belong to a man who departs from JFK. Thus, the itinerary must begin with JFK. + +Note: +If there are multiple valid itineraries, you should return the itinerary that has the smallest lexical order when read +as a single string. For example, the itinerary ["JFK", "LGA"] has a smaller lexical order than ["JFK", "LGB"]. +All airports are represented by three capital letters (IATA code). +You may assume all tickets form at least one valid itinerary. +Example 1: +tickets = [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]] +Return ["JFK", "MUC", "LHR", "SFO", "SJC"]. +Example 2: +tickets = [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]] +Return ["JFK","ATL","JFK","SFO","ATL","SFO"]. +Another possible reconstruction is ["JFK","SFO","ATL","JFK","ATL","SFO"]. But it is larger in lexical order. +""" +import heapq +from collections import defaultdict, deque + +__author__ = 'Daniel' + + +class Solution(object): + def findItinerary(self, tickets): + """ + Euler path + Hierholzer's algorithm a Euler path, must be directed graph + The graph must be directed graph + :type tickets: List[List[str]] + :rtype: List[str] + """ + G = defaultdict(list) + for elt in tickets: + s, e = elt + heapq.heappush(G[s], e) + + ret = deque() + self.dfs(G, 'JFK', ret) + return list(ret) + + def dfs(self, G, cur, ret): + while G[cur]: + self.dfs(G, heapq.heappop(G[cur]), ret) + ret.appendleft(cur) + + +if __name__ == "__main__": + assert Solution().findItinerary([["JFK","KUL"],["JFK","NRT"],["NRT","JFK"]]) == ['JFK', 'NRT', 'JFK', 'KUL'] + + + + + + From 03ddce186fea7b06792cdbb89655bc9c3008a02b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 2 Sep 2016 22:45:12 -0700 Subject: [PATCH 162/585] update reservoir sampling --- 332 Reconstruct Itinerary.py | 2 +- 382 Linked List Random Node.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/332 Reconstruct Itinerary.py b/332 Reconstruct Itinerary.py index 85caa7a..1bdf457 100644 --- a/332 Reconstruct Itinerary.py +++ b/332 Reconstruct Itinerary.py @@ -26,7 +26,7 @@ def findItinerary(self, tickets): """ Euler path Hierholzer's algorithm a Euler path, must be directed graph - The graph must be directed graph + The graph must be directed graph :type tickets: List[List[str]] :rtype: List[str] """ diff --git a/382 Linked List Random Node.py b/382 Linked List Random Node.py index f7e7ada..f4d7b1d 100644 --- a/382 Linked List Random Node.py +++ b/382 Linked List Random Node.py @@ -48,7 +48,7 @@ def getRandom(self): cur = self.head.next idx = 1 while cur: - if random.randrange(0, idx+1) == idx: + if random.randrange(0, idx+1) == 0: ret = cur cur = cur.next idx += 1 From 651ab6edfbae61701e53b79fc0e7915dfdea8e9a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 3 Sep 2016 16:45:45 -0700 Subject: [PATCH 163/585] heap --- 377 Combination Sum IV.py | 56 +++++++++++++ ...Kth Smallest Element in a Sorted Matrix.py | 83 +++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 377 Combination Sum IV.py create mode 100644 378 Kth Smallest Element in a Sorted Matrix.py diff --git a/377 Combination Sum IV.py b/377 Combination Sum IV.py new file mode 100644 index 0000000..19533ae --- /dev/null +++ b/377 Combination Sum IV.py @@ -0,0 +1,56 @@ +""" +Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up +to a positive integer target. + +Example: + +nums = [1, 2, 3] +target = 4 + +The possible combination ways are: +(1, 1, 1, 1) +(1, 1, 2) +(1, 2, 1) +(1, 3) +(2, 1, 1) +(2, 2) +(3, 1) + +Note that different sequences are counted as different combinations. + +Therefore the output is 7. +Follow up: +What if negative numbers are allowed in the given array? +How does it change the problem? +What limitation we need to add to the question to allow negative numbers? +""" +__author__ = 'Daniel' + + +class Solution(object): + def combinationSum4(self, nums, target): + """ + Let F[i] be the number of combinations ways for number i + F[i] = sum(F[i-k] for k in nums) + + If negative number allowed, [1, -1], 1, then infinite combinations. + If the target is large, recursion & memoization is more space saving + :type nums: List[int] + :type target: int + :rtype: int + """ + F = [0 for _ in xrange(target + 1)] + nums = filter(lambda x: x <= target, nums) + for k in nums: + F[k] = 1 + + for i in xrange(target + 1): + for k in nums: + if i - k >= 0: + F[i] += F[i-k] + + return F[target] + + +if __name__ == "__main__": + assert Solution().combinationSum4([1, 2, 3], 4) == 7 \ No newline at end of file diff --git a/378 Kth Smallest Element in a Sorted Matrix.py b/378 Kth Smallest Element in a Sorted Matrix.py new file mode 100644 index 0000000..eee6ac0 --- /dev/null +++ b/378 Kth Smallest Element in a Sorted Matrix.py @@ -0,0 +1,83 @@ +""" +Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in +the matrix. + +Note that it is the kth smallest element in the sorted order, not the kth distinct element. + +Example: + +matrix = [ + [ 1, 5, 9], + [10, 11, 13], + [12, 13, 15] +], +k = 8, + +return 13. +Note: +You may assume k is always valid, 1 <= k <= n2. +""" +import heapq + + +__author__ = 'Daniel' + + +class Solution(object): + def kthSmallest(self, matrix, k): + """ + Heap of list + :type matrix: List[List[int]] + :type k: int + :rtype: int + """ + m, n = len(matrix), len(matrix[0]) + + class Value(object): + def __init__(self, i, j): + self.i = i + self.j = j + + def __cmp__(self, other): + return matrix[self.i][self.j] - matrix[other.i][other.j] + + def hasnext(self): + return self.j+1 < n + + def next(self): + if self.hasnext(): + return Value(self.i, self.j + 1) + + raise StopIteration + + h = [] + for i in xrange(m): + heapq.heappush(h, Value(i, 0)) + + ret = None + for _ in xrange(k): + ret = heapq.heappop(h) + if ret.hasnext(): + heapq.heappush(h, ret.next()) + + return matrix[ret.i][ret.j] + + def kthSmallestError(self, matrix, k): + """ + :type matrix: List[List[int]] + :type k: int + :rtype: int + """ + m, n = len(matrix), len(matrix[0]) + i = k % n + j = k - (i * m) + return matrix[i][j] + +if __name__ == "__main__": + matrix = [ + [1, 5, 9], + [10, 11, 13], + [12, 13, 15] + ] + k = 8 + print Solution().kthSmallest(matrix, k) From e7d054c4542252d5ce00b93a76d7f560502cbe6a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 3 Sep 2016 17:38:41 -0700 Subject: [PATCH 164/585] heap --- 373 Find K Pairs with Smallest Sums.py | 107 ++++++++++++++++++ ...Kth Smallest Element in a Sorted Matrix.py | 6 +- 2 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 373 Find K Pairs with Smallest Sums.py diff --git a/373 Find K Pairs with Smallest Sums.py b/373 Find K Pairs with Smallest Sums.py new file mode 100644 index 0000000..904b0c2 --- /dev/null +++ b/373 Find K Pairs with Smallest Sums.py @@ -0,0 +1,107 @@ +""" +You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. + +Define a pair (u,v) which consists of one element from the first array and one element from the second array. + +Find the k pairs (u1,v1),(u2,v2) ...(uk,vk) with the smallest sums. + +Example 1: +Given nums1 = [1,7,11], nums2 = [2,4,6], k = 3 + +Return: [1,2],[1,4],[1,6] + +The first 3 pairs are returned from the sequence: +[1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6] +Example 2: +Given nums1 = [1,1,2], nums2 = [1,2,3], k = 2 + +Return: [1,1],[1,1] + +The first 2 pairs are returned from the sequence: +[1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3] +Example 3: +Given nums1 = [1,2], nums2 = [3], k = 3 + +Return: [1,3],[2,3] + +All possible pairs are returned from the sequence: +[1,3],[2,3] +""" +import heapq + +__author__ = 'Daniel' + + +class Solution(object): + def kSmallestPairs(self, nums1, nums2, k): + """ + Maintain a heap of the k pairs + The art is how to select the next pair. + + O(k log k) + + https://discuss.leetcode.com/topic/50885/simple-java-o-klogk-solution-with-explanation + :type nums1: List[int] + :type nums2: List[int] + :type k: int + :rtype: List[List[int]] + """ + class Node(object): + def __init__(self, i, j): + self.i, self.j = i, j + + def __cmp__(self, other): + return nums1[self.i] + nums2[self.j] - (nums1[other.i] + nums2[other.j]) + + def hasnext(self): + return self.j + 1 < len(nums2) + + def next(self): + if self.hasnext(): + return Node(self.i, self.j + 1) + + raise StopIteration + + if not nums1 or not nums2: + return [] + + h = [] + for i in xrange(min(k, len(nums1))): + heapq.heappush(h, Node(i, 0)) + + ret = [] + while h and len(ret) < k: + node = heapq.heappop(h) + ret.append([nums1[node.i], nums2[node.j]]) + if node.hasnext(): + heapq.heappush(h, node.next()) + + return ret + + def kSmallestPairsError(self, nums1, nums2, k): + """ + The merge process for merge sort + :type nums1: List[int] + :type nums2: List[int] + :type k: int + :rtype: List[List[int]] + """ + i = 0 + j = 0 + ret = [] + for _ in xrange(k): + if i < len(nums1) and j < len(nums2): + ret.append([nums1[i], nums2[j]]) + if nums1[i] < nums2[j]: + j += 1 + else: + i += 1 + else: + break + + return ret + + +if __name__ == "__main__": + assert Solution().kSmallestPairs([1, 7, 11], [2, 4, 6], 9) == [[1, 2], [1, 4], [1, 6], [7, 2], [7, 4], [11, 2], + [7, 6], [11, 4], [11, 6]] \ No newline at end of file diff --git a/378 Kth Smallest Element in a Sorted Matrix.py b/378 Kth Smallest Element in a Sorted Matrix.py index eee6ac0..810402b 100644 --- a/378 Kth Smallest Element in a Sorted Matrix.py +++ b/378 Kth Smallest Element in a Sorted Matrix.py @@ -33,7 +33,7 @@ def kthSmallest(self, matrix, k): """ m, n = len(matrix), len(matrix[0]) - class Value(object): + class Node(object): def __init__(self, i, j): self.i = i self.j = j @@ -46,13 +46,13 @@ def hasnext(self): def next(self): if self.hasnext(): - return Value(self.i, self.j + 1) + return Node(self.i, self.j + 1) raise StopIteration h = [] for i in xrange(m): - heapq.heappush(h, Value(i, 0)) + heapq.heappush(h, Node(i, 0)) ret = None for _ in xrange(k): From ff0cb84ffa0fcff38387a51a1703604fab22a13d Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 3 Sep 2016 18:31:26 -0700 Subject: [PATCH 165/585] dp & number theory --- 368 Largest Divisible Subset.py | 66 +++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 368 Largest Divisible Subset.py diff --git a/368 Largest Divisible Subset.py b/368 Largest Divisible Subset.py new file mode 100644 index 0000000..65c0dc5 --- /dev/null +++ b/368 Largest Divisible Subset.py @@ -0,0 +1,66 @@ +""" +Given a set of distinct positive integers, find the largest subset such that every pair (Si, Sj) of elements in this +subset satisfies: Si % Sj = 0 or Sj % Si = 0. + +If there are multiple solutions, return any subset is fine. + +Example 1: + +nums: [1,2,3] + +Result: [1,2] (of course, [1,3] will also be ok) +Example 2: + +nums: [1,2,4,8] + +Result: [1,2,4,8] +""" +from collections import deque + +__author__ = 'Daniel' + + +class Solution(object): + def largestDivisibleSubset(self, A): + """ + Given a divisible subset, when adding a new number, we only needs to validate whether the new number is + divisible by the largest number in the divisible subset. + + Let F[i] for the size of subset ended with A[i] + F[i] = max(1 + F[j] if A[i] % A[j] for j in xrange(i-1)) + pi[i] = argmax(...) + :type A: List[int] + :rtype: List[int] + """ + if not A: + return [] + + F = {} + pi = {} + A.sort() + for i in xrange(len(A)): + F[i] = 1 + pi[i] = i + for j in xrange(i): + if A[i] % A[j] == 0: + if F[i] < 1 + F[j]: + F[i] = 1 + F[j] + pi[i] = j + + max_i, max_v = 0, 1 + for k, v in F.items(): + if v > max_v: + max_i, max_v = k, v + + ret = deque() + cur = max_i + ret.appendleft(A[cur]) + while pi[cur] != cur: + cur = pi[cur] + ret.appendleft(A[cur]) + + return list(ret) + +if __name__ == "__main__": + assert Solution().largestDivisibleSubset([1, 2, 4, 8]) == [1, 2, 4, 8] + From 771c8c89aca4b9fd45661de2300e0bea0dd1ff55 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 3 Sep 2016 20:55:17 -0700 Subject: [PATCH 166/585] power mod --- 368 Largest Divisible Subset.py | 4 +-- 372 Super Pow.py | 50 +++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 372 Super Pow.py diff --git a/368 Largest Divisible Subset.py b/368 Largest Divisible Subset.py index 65c0dc5..d8be3f2 100644 --- a/368 Largest Divisible Subset.py +++ b/368 Largest Divisible Subset.py @@ -32,8 +32,7 @@ def largestDivisibleSubset(self, A): :type A: List[int] :rtype: List[int] """ - if not A: - return [] + if not A: return [] F = {} pi = {} @@ -61,6 +60,7 @@ def largestDivisibleSubset(self, A): return list(ret) + if __name__ == "__main__": assert Solution().largestDivisibleSubset([1, 2, 4, 8]) == [1, 2, 4, 8] diff --git a/372 Super Pow.py b/372 Super Pow.py new file mode 100644 index 0000000..4098e9b --- /dev/null +++ b/372 Super Pow.py @@ -0,0 +1,50 @@ +""" +Your task is to calculate a^b mod 1337 where a is a positive integer and b is an extremely large positive integer given +in the form of an array. + +Example1: + +a = 2 +b = [3] + +Result: 8 +Example2: + +a = 2 +b = [1,0] + +Result: 1024 +""" +__author__ = 'Daniel' + +C = 1337 + + +class Solution(object): + def superPow(self, a, b): + """ + a^123 = a^120 * a^3 + = a^12 ^ 10 * a^3 + + Power math. + :type a: int + :type b: List[int] + :rtype: int + """ + if not b: + return 1 + s = 1 + lsd = b.pop() # list significant digit + s *= (a % C) ** lsd + s %= C + rest = self.superPow(a, b) + s *= rest ** 10 + s %= C + return s + +if __name__ == "__main__": + print Solution().superPow(2, [1, 0]) + + + + From 7476294ec3ba3672c621fe4108470d80d8fe0204 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 3 Sep 2016 20:55:45 -0700 Subject: [PATCH 167/585] power mod --- 372 Super Pow.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/372 Super Pow.py b/372 Super Pow.py index 4098e9b..e64939b 100644 --- a/372 Super Pow.py +++ b/372 Super Pow.py @@ -23,6 +23,8 @@ class Solution(object): def superPow(self, a, b): """ + since b is given as a list rather than a number, we need to process it digit by digit. + a^123 = a^120 * a^3 = a^12 ^ 10 * a^3 From aeca4bd6081cab16adf31033c9c63d2e203c8a65 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 3 Sep 2016 22:01:09 -0700 Subject: [PATCH 168/585] string parsing --- 372 Super Pow.py | 2 +- 385 Mini Parser Nested Integer.py | 123 ++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 385 Mini Parser Nested Integer.py diff --git a/372 Super Pow.py b/372 Super Pow.py index e64939b..19d6064 100644 --- a/372 Super Pow.py +++ b/372 Super Pow.py @@ -23,7 +23,7 @@ class Solution(object): def superPow(self, a, b): """ - since b is given as a list rather than a number, we need to process it digit by digit. + since b is given as a list rather than a number, we need to process it digit by digit. a^123 = a^120 * a^3 = a^12 ^ 10 * a^3 diff --git a/385 Mini Parser Nested Integer.py b/385 Mini Parser Nested Integer.py new file mode 100644 index 0000000..a7da4df --- /dev/null +++ b/385 Mini Parser Nested Integer.py @@ -0,0 +1,123 @@ +""" +Given a nested list of integers represented as a string, implement a parser to deserialize it. + +Each element is either an integer, or a list -- whose elements may also be integers or other lists. + +Note: You may assume that the string is well-formed: + +String is non-empty. +String does not contain white spaces. +String contains only digits 0-9, [, - ,, ]. +Example 1: + +Given s = "324", + +You should return a NestedInteger object which contains a single integer 324. +Example 2: + +Given s = "[123,[456,[789]]]", + +Return a NestedInteger object containing a nested list with 2 elements: + +1. An integer containing value 123. +2. A nested list containing two elements: + i. An integer containing value 456. + ii. A nested list with one element: + a. An integer containing value 789. +""" +__author__ = 'Daniel' + +# """ +# This is the interface that allows for creating nested lists. +# You should not implement it, or speculate about its implementation +# """ + + +class NestedInteger(object): + def __init__(self, value=None): + """ + If value is not specified, initializes an empty list. + Otherwise initializes a single integer equal to value. + """ + def isInteger(self): + """ + @return True if this NestedInteger holds a single integer, rather than a nested list. + :rtype bool + """ + + def add(self, elem): + """ + Set this NestedInteger to hold a nested list and adds a nested integer elem to it. + :rtype void + """ + + def setInteger(self, value): + """ + Set this NestedInteger to hold a single integer equal to value. + :rtype void + """ + + def getInteger(self): + """ + @return the single integer that this NestedInteger holds, if it holds a single integer + Return None if this NestedInteger holds a nested list + :rtype int + """ + + def getList(self): + """ + @return the nested list that this NestedInteger holds, if it holds a nested list + Return None if this NestedInteger holds a single integer + :rtype List[NestedInteger] + """ + + +class Solution(object): + def deserialize(self, s): + """ + NestedInteger is a UnionType in functional programming jargon. + + [1, [1, [2]], 3, 4] + From a general example, develop an algorithm using stack + The algorithm itself is easy, but the string parsing contains lots of edge cases + :type s: str + :rtype: NestedInteger + """ + if not s: return None + stk = [] + + i = 0 + while i < len(s): + if s[i] == '[': + stk.append(NestedInteger()) + i += 1 + elif s[i] == ']': + ni = stk.pop() + if not stk: return ni + + stk[-1].add(ni) + i += 1 + elif s[i] == ',': + i += 1 + else: + j = i + while j < len(s) and (s[j].isdigit() or s[j] == '-'): j += 1 + + ni = NestedInteger(int(s[i: j]) if s[i: j] else None) + if not stk: return ni + stk[-1].add(ni) + i = j + + return stk.pop() + + +if __name__ == "__main__": + Solution().deserialize("[123,[456,[789]]]") + + + + + + + + From 6d6059e2b8db039e419fa1615f51446b5d0ad015 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 4 Sep 2016 11:28:42 -0700 Subject: [PATCH 169/585] basic --- 339 Nested List Weight Sum.py | 56 ++++++++++++++++++++++++++ 346 Moving Average from Data Stream.py | 34 ++++++++++++++++ 359 Logger Rate Limiter.py | 33 +++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 339 Nested List Weight Sum.py create mode 100644 346 Moving Average from Data Stream.py create mode 100644 359 Logger Rate Limiter.py diff --git a/339 Nested List Weight Sum.py b/339 Nested List Weight Sum.py new file mode 100644 index 0000000..b3060ec --- /dev/null +++ b/339 Nested List Weight Sum.py @@ -0,0 +1,56 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +""" +This is the interface that allows for creating nested lists. +You should not implement it, or speculate about its implementation +""" + + +class NestedInteger(object): + def isInteger(self): + """ + @return True if this NestedInteger holds a single integer, rather than a nested list. + :rtype bool + """ + + def getInteger(self): + """ + @return the single integer that this NestedInteger holds, if it holds a single integer + Return None if this NestedInteger holds a nested list + :rtype int + """ + + def getList(self): + """ + @return the nested list that this NestedInteger holds, if it holds a nested list + Return None if this NestedInteger holds a single integer + :rtype List[NestedInteger] + """ + + +class Solution(object): + def __init__(self): + self.sum = 0 + + def depthSum(self, nestedList): + """ + NestedInteger is a union type + :type nestedList: List[NestedInteger] + :rtype: int + """ + for elt in nestedList: + self.dfs(elt, 1) + + return self.sum + + def dfs(self, ni, depth): + if ni.isInteger(): + self.sum += ni.getInteger() * depth + else: + lst = ni.getList() + for elt in lst: + self.dfs(elt, depth + 1) diff --git a/346 Moving Average from Data Stream.py b/346 Moving Average from Data Stream.py new file mode 100644 index 0000000..6407d79 --- /dev/null +++ b/346 Moving Average from Data Stream.py @@ -0,0 +1,34 @@ +""" +Premium Question +""" +from collections import deque + +__author__ = 'Daniel' + + +class MovingAverage(object): + def __init__(self, size): + """ + Initialize your data structure here. + :type size: int + """ + self.size = size + self.q = deque() + self.sum = 0 + + def next(self, val): + """ + :type val: int + :rtype: float + """ + self.q.append(val) + self.sum += val + if len(self.q) > self.size: + self.sum -= self.q.popleft() + + return float(self.sum) / len(self.q) + + +# Your MovingAverage object will be instantiated and called as such: +# obj = MovingAverage(size) +# param_1 = obj.next(val) \ No newline at end of file diff --git a/359 Logger Rate Limiter.py b/359 Logger Rate Limiter.py new file mode 100644 index 0000000..e612ea7 --- /dev/null +++ b/359 Logger Rate Limiter.py @@ -0,0 +1,33 @@ +""" +Premium question +""" +__author__ = 'Daniel' + + +class Logger(object): + def __init__(self): + """ + Initialize your data structure here. + """ + self.h = {} + + + def shouldPrintMessage(self, timestamp, message): + """ + Returns true if the message should be printed in the given timestamp, otherwise returns false. + If this method returns false, the message will not be printed. + The timestamp is in seconds granularity. + :type timestamp: int + :type message: str + :rtype: bool + """ + if message not in self.h or timestamp - self.h[message] >= 10: + self.h[message] = timestamp + return True + + return False + + +# Your Logger object will be instantiated and called as such: +# obj = Logger() +# param_1 = obj.shouldPrintMessage(timestamp,message) \ No newline at end of file From 7091dd57c89083e54fcdf0e4f868edc1d23ea6bd Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 4 Sep 2016 12:22:23 -0700 Subject: [PATCH 170/585] height of tree --- 366 Find Leaves of Binary Tree.py | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 366 Find Leaves of Binary Tree.py diff --git a/366 Find Leaves of Binary Tree.py b/366 Find Leaves of Binary Tree.py new file mode 100644 index 0000000..df22de9 --- /dev/null +++ b/366 Find Leaves of Binary Tree.py @@ -0,0 +1,42 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def findLeaves(self, root): + """ + The key is + 1. to find height of a tree + 2. to maintain a leaves nested list + + The height of a node is the number of edges from the node to the deepest leaf. + :type root: TreeNode + :rtype: List[List[int]] + """ + leaves = [] + self.dfs(root, leaves) + return leaves + + def dfs(self, node, leaves): + """ + :return: height of of a node + """ + if not node: + return -1 # leaves index start from 0 + + height = 1 + max(self.dfs(node.left, leaves), self.dfs(node.right, leaves)) + if height >= len(leaves): + leaves.append([]) # grow + + leaves[height].append(node.val) + return height From e05d6fa0c3afa70fb89e28b08bd4cd68930980c1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 5 Sep 2016 16:21:47 -0700 Subject: [PATCH 171/585] delta --- 370 Range Addition.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 370 Range Addition.py diff --git a/370 Range Addition.py b/370 Range Addition.py new file mode 100644 index 0000000..f867c52 --- /dev/null +++ b/370 Range Addition.py @@ -0,0 +1,37 @@ +""" +Premium question +""" +__author__ = 'Daniel' + + +class Solution(object): + def getModifiedArray(self, length, updates): + """ + Brute force: O(kn) + + Algorithm: Complement + [i, j] increases by delta is equivalent to [i, n) increase delta and [j+1, n) decreases by delta; thus we only + need to update two positions O(1) rather than update the entire range [i, j] in O(n) + + ...++++.... + ...++++++++ + .......---- + + Complexity: O(k + n) + + :type length: int + :type updates: List[List[int]] + :rtype: List[int] + """ + deltas = [0 for _ in xrange(length)] + for i, j, k in updates: + deltas[i] += k + if j + 1 < length: deltas[j + 1] -= k + + ret = [] + acc = 0 + for delta in deltas: + acc += delta + ret.append(acc) + + return ret From af788bb08f97a4a935d123d1b924f1338c9c068e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 5 Sep 2016 16:42:27 -0700 Subject: [PATCH 172/585] reverse linked list --- 369 Plus One Linked List.py | 57 +++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 369 Plus One Linked List.py diff --git a/369 Plus One Linked List.py b/369 Plus One Linked List.py new file mode 100644 index 0000000..9a54755 --- /dev/null +++ b/369 Plus One Linked List.py @@ -0,0 +1,57 @@ +""" +Premium question +""" +__author__ = 'Daniel' + + +# Definition for singly-linked list. +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + + +class Solution(object): + def plusOne(self, head): + """ + reverse, plus one, then reverse + :type head: ListNode + :rtype: ListNode + """ + head = self.revserse(head) + head = self.plus(head) + head = self.revserse(head) + return head + + def plus(self, head): + cur = head + while cur: + cur.val += 1 + if cur.val >= 10: + cur.val -= 10 + if not cur.next: + cur.next = ListNode(0) + cur = cur.next + else: + break + + return head + + def revserse(self, head): + if not head: + return None + + dummy = ListNode(0) + dummy.next = head + pre = dummy + cur = pre.next + while pre and cur: + nxt = cur.next + + cur.next = pre + + pre = cur + cur = nxt + + dummy.next.next = None # original head + return pre From e302a1d822f5cd0d8772b6bf3c78b456217e443c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 5 Sep 2016 16:59:43 -0700 Subject: [PATCH 173/585] hit counter --- 362 Design Hit Counter.py | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 362 Design Hit Counter.py diff --git a/362 Design Hit Counter.py b/362 Design Hit Counter.py new file mode 100644 index 0000000..f70eb7f --- /dev/null +++ b/362 Design Hit Counter.py @@ -0,0 +1,48 @@ +""" +Premium Question +""" +from collections import deque + +__author__ = 'Daniel' + + +class HitCounter(object): + def __init__(self): + """ + Initialize your data structure here. + + calls are being made to the system in chronological order. + It is possible that several hits arrive roughly at the same time. + What if the number of hits per second could be very large? Does your design scale? # use counter + """ + self.q = deque() + + def hit(self, timestamp): + """ + Record a hit. + @param timestamp - The current timestamp (in seconds granularity). + :type timestamp: int + :rtype: void + """ + self.pop(timestamp) + self.q.append(timestamp) + + def getHits(self, timestamp): + """ + Return the number of hits in the past 5 minutes. + @param timestamp - The current timestamp (in seconds granularity). + :type timestamp: int + :rtype: int + """ + self.pop(timestamp) + return len(self.q) + + def pop(self, timestamp): + while self.q and timestamp - self.q[0] >= 300: + self.q.popleft() + + +# Your HitCounter object will be instantiated and called as such: +# obj = HitCounter() +# obj.hit(timestamp) +# param_2 = obj.getHits(timestamp) \ No newline at end of file From 483a52e628f9cfc5af0b5dd619528c938f2a5dcc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 5 Sep 2016 23:25:24 -0700 Subject: [PATCH 174/585] nested integer inverse sum --- 364 Nested List Weight Sum II.py | 118 +++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 364 Nested List Weight Sum II.py diff --git a/364 Nested List Weight Sum II.py b/364 Nested List Weight Sum II.py new file mode 100644 index 0000000..76ae1fb --- /dev/null +++ b/364 Nested List Weight Sum II.py @@ -0,0 +1,118 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +""" +This is the interface that allows for creating nested lists. +You should not implement it, or speculate about its implementation +""" + + +class NestedInteger(object): + def __init__(self, value=None): + """ + If value is not specified, initializes an empty list. + Otherwise initializes a single integer equal to value. + """ + + def isInteger(self): + """ + @return True if this NestedInteger holds a single integer, rather than a nested list. + :rtype bool + """ + + def add(self, elem): + """ + Set this NestedInteger to hold a nested list and adds a nested integer elem to it. + :rtype void + """ + + def setInteger(self, value): + """ + Set this NestedInteger to hold a single integer equal to value. + :rtype void + """ + + def getInteger(self): + """ + @return the single integer that this NestedInteger holds, if it holds a single integer + Return None if this NestedInteger holds a nested list + :rtype int + """ + + def getList(self): + """ + @return the nested list that this NestedInteger holds, if it holds a nested list + Return None if this NestedInteger holds a single integer + :rtype List[NestedInteger] + """ + + +class Solution(object): + def __init__(self): + self.sum = 0 + + def depthSumInverse(self, nestedList): + """ + NestedInteger is a union type + :type nestedList: List[NestedInteger] + :rtype: int + """ + inv_depth = self.height(nestedList) + self.inverseDepthSum(nestedList, inv_depth) + return self.sum + + def height(self, nl): + nl_lst = filter(lambda x: not x.isInteger(), nl) + if not nl_lst: + return 1 + if nl_lst: + return 1 + max( + map(lambda x: self.height(x.getList()), nl_lst) + ) + + def inverseDepthSum(self, nl, inv_depth): + nl_lst = filter(lambda x: not x.isInteger(), nl) + ni_list = filter(lambda x: x.isInteger(), nl) + if nl_lst: + map(lambda x: self.inverseDepthSum(x.getList(), inv_depth - 1), nl_lst) + if ni_list: + self.sum += sum(map(lambda x: x.getInteger() * inv_depth, ni_list)) + + +class SolutionError(object): + def __init__(self): + self.sum = 0 + + def depthSumInverse(self, nestedList): + """ + NestedInteger is a union type + :type nestedList: List[NestedInteger] + :rtype: int + """ + self.dfs(nestedList) + return self.sum + + def dfs(self, nl): + """ + This dfs use height: the number of edges from to the leaves. + But the question is supposedly use height but the calculate sum top down; here is bottom up wrongly. + """ + height = 1 + + nl_lst = filter(lambda x: not x.isInteger(), nl) + ni_list = filter(lambda x: x.isInteger(), nl) + if nl_lst: + height = 1 + max( + map(lambda x: self.dfs(x.getList()), nl_lst) + ) + if ni_list: + self.sum += sum(map(lambda x: x.getInteger() * height, ni_list)) + + return height + + + + From f147160bf03d12e275761bf9e88469f3f67adebc Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 6 Sep 2016 10:13:23 -0700 Subject: [PATCH 175/585] greedy --- 392 Is Subsequence.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 392 Is Subsequence.py diff --git a/392 Is Subsequence.py b/392 Is Subsequence.py new file mode 100644 index 0000000..ab8534d --- /dev/null +++ b/392 Is Subsequence.py @@ -0,0 +1,40 @@ +""" +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. +""" +__author__ = 'Daniel' + + +class Solution(object): + def isSubsequence(self, s, t): + """ + Greedy matching + :type s: str + :type t: str + :rtype: bool + """ + i = 0 + j = 0 + while i < len(s) and j < len(t): + if t[j] != s[i]: + j += 1 + else: + i += 1 + j += 1 + + return i == len(s) From a7eee4e5a9f653591679c6c9e3922fabe967b88a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 6 Sep 2016 13:42:29 -0700 Subject: [PATCH 176/585] stack & dfs --- 394 Decode String.py | 119 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 394 Decode String.py diff --git a/394 Decode String.py b/394 Decode String.py new file mode 100644 index 0000000..896d704 --- /dev/null +++ b/394 Decode String.py @@ -0,0 +1,119 @@ +""" +Given an encoded string, return it's decoded string. + +The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is being repeated exactly +k times. Note that k is guaranteed to be a positive integer. + +You may assume that the input string is always valid; No extra white spaces, square brackets are well-formed, etc. + +Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat +numbers, k. For example, there won't be input like 3a or 2[4]. + +Examples: + +s = "3[a]2[bc]", return "aaabcbc". +s = "3[a2[c]]", return "accaccacc". +s = "2[abc]3[cd]ef", return "abcabccdcdcdef". +""" +__author__ = 'Daniel' + + +class Solution(object): + def decodeString(self, s): + """ + :type s: str + :rtype: str + """ + stk = [ + [1, []] + ] # with default + i = 0 + while i < len(s): + if s[i].isdigit(): # construct number from digit + j = i+1 + while s[j] != '[': j += 1 + stk.append([ + int(s[i:j]), [] + ]) + i = j+1 + elif s[i].islower(): # append alphabet + stk[-1][1].append(s[i]) + i += 1 + elif s[i] == ']': # pop + cnt, partial = stk.pop() + partial = ''.join(partial) * cnt + stk[-1][1].append(partial) + i += 1 + + return ''.join(stk.pop()[1]) + + +class SolutionVerbose(object): + def decodeString(self, s): + """ + :type s: str + :rtype: str + """ + stk = [] + i = 0 + ret = [] + while i < len(s): + if s[i].isdigit(): # construct number from digit + j = i+1 + while s[j] != '[': j += 1 + stk.append([ + int(s[i:j]), [] + ]) + i = j+1 + elif s[i].islower(): # append alphabet + if not stk: + ret.append(s[i]) + else: + stk[-1][1].append(s[i]) + i += 1 + elif s[i] == ']': # pop + cnt, partial = stk.pop() + partial = ''.join(partial) * cnt + if not stk: + ret.append(partial) + else: + stk[-1][1].append(partial) + + i += 1 + + return ''.join(ret) + + +class SolutionError(object): + def decodeString(self, s): + """ + :type s: str + :rtype: str + """ + stk = [] + i = 0 + ret = [] + while i < len(s): + if s[i].isdigit(): + j = i + 1 + while s[j] != '[': j += 1 + prev = stk[-1] if stk else 1 + stk.append(prev * int(s[i:j])) + i = j + 1 + elif s[i].islower(): + repeat = stk[-1] if stk else 1 + for _ in xrange(repeat): ret.append(s[i]) + i += 1 + elif s[i] == ']': + stk.pop() + i += 1 + + return ''.join(ret) + + +if __name__ == "__main__": + print Solution().decodeString('2[abc]3[cd]ef') + + + + From e271fc8b3f055ead261208660af0e1f0ae3d17c9 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 6 Sep 2016 14:14:12 -0700 Subject: [PATCH 177/585] math, quadratic function --- 360 Sort Transformed Array.py | 56 +++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 360 Sort Transformed Array.py diff --git a/360 Sort Transformed Array.py b/360 Sort Transformed Array.py new file mode 100644 index 0000000..3bd9564 --- /dev/null +++ b/360 Sort Transformed Array.py @@ -0,0 +1,56 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + +import bisect + + +class Solution(object): + def sortTransformedArray(self, nums, a, b, c): + """ + Key points: + Quadratic function + + Pitfalls: + 1. case when a = 0 + 2. whether need to reverse the string + :type nums: List[int] + :type a: int + :type b: int + :type c: int + :rtype: List[int] + """ + if a == 0: + ret = map(lambda x: self.f(x, a, b, c), nums) + return ret if b > 0 else ret[::-1] + + mid = - float(b) / (2*a) + ri = bisect.bisect_left(nums, mid) + le = ri - 1 + ret = [] + while le >= 0 and ri < len(nums) and le < ri: + f_le = self.f(nums[le], a, b, c) + f_ri = self.f(nums[ri], a, b, c) + if a > 0 and f_le < f_ri or a < 0 and f_le > f_ri: + ret.append(f_le) + le -= 1 + else: + ret.append(f_ri) + ri += 1 + + while le >= 0: + ret.append(self.f(nums[le], a, b, c)) + le -= 1 + while ri < len(nums): + ret.append(self.f(nums[ri], a, b, c)) + ri += 1 + + return ret if a > 0 else ret[::-1] + + def f(self, x, a, b, c): + return a * (x ** 2) + b * x + c + + +if __name__ == "__main__": + assert Solution().sortTransformedArray([-4, -2, 2, 4], -1, 3, 5) == [-23, -5, 1, 7] \ No newline at end of file From a63369b0415538f959ede29f93a49f912c4a98d6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 6 Sep 2016 14:40:03 -0700 Subject: [PATCH 178/585] math problem --- 365 Water and Jug Problem.py | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 365 Water and Jug Problem.py diff --git a/365 Water and Jug Problem.py b/365 Water and Jug Problem.py new file mode 100644 index 0000000..fff3387 --- /dev/null +++ b/365 Water and Jug Problem.py @@ -0,0 +1,46 @@ +""" +You are given two jugs with capacities x and y litres. There is an infinite amount of water supply available. You need +to determine whether it is possible to measure exactly z litres using these two jugs. + +If z liters of water is measurable, you must have z liters of water contained within one or both buckets by the end. + +Operations allowed: + +Fill any of the jugs completely with water. +Empty any of the jugs. +Pour water from one jug into another till the other jug is completely full or the first jug itself is empty. +Example 1: (From the famous "Die Hard" example) + +Input: x = 3, y = 5, z = 4 +Output: True +Example 2: + +Input: x = 2, y = 6, z = 5 +Output: False +""" +__author__ = 'Daniel' + + +class Solution(object): + def canMeasureWater(self, x, y, z): + """ + Number theory + Use the property of Bezout's identity and check if z is a multiple of GCD(x, y) + https://discuss.leetcode.com/topic/49238/math-solution-java-solution + + ax + by = d + :type x: int + :type y: int + :type z: int + :rtype: bool + """ + if x + y < z: return False + if x == z or y == z: return True + + return z % self.gcd(x, y) == 0 + + def gcd(self, a, b): + while b: + a, b = b, a%b + return a + From f2a2507cdd35b9ac8147f8fea1e4a42a3558adbb Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 6 Sep 2016 15:24:31 -0700 Subject: [PATCH 179/585] D & C --- ...ng with At Least K Repeating Characters.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 395 Longest Substring with At Least K Repeating Characters.py diff --git a/395 Longest Substring with At Least K Repeating Characters.py b/395 Longest Substring with At Least K Repeating Characters.py new file mode 100644 index 0000000..9d6c7fb --- /dev/null +++ b/395 Longest Substring with At Least K Repeating Characters.py @@ -0,0 +1,53 @@ +""" +Find the length of the longest substring T of a given string (consists of lowercase letters only) such that every +character in T appears no less than k times. + +Example 1: + +Input: +s = "aaabb", k = 3 + +Output: +3 + +The longest substring is "aaa", as 'a' is repeated 3 times. +Example 2: + +Input: +s = "ababbc", k = 2 + +Output: +5 + +The longest substring is "ababb", as 'a' is repeated 2 times and 'b' is repeated 3 times. +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class Solution(object): + def longestSubstring(self, s, k): + """ + D & C, forming boundary by the letter of min count + :type s: str + :type k: int + :rtype: int + """ + if not s: + return 0 + + cnt = defaultdict(int) + for e in s: cnt[e] += 1 + + c = min( + s, + key=lambda x: cnt[x], + ) + + if cnt[c] >= k: + return len(s) + + return max( + map(lambda x: self.longestSubstring(x, k), s.split(c)) + ) From 47ab407457fea315ab1ad690971f4272869b5e1a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 6 Sep 2016 15:26:42 -0700 Subject: [PATCH 180/585] add assertion --- 394 Decode String.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/394 Decode String.py b/394 Decode String.py index 896d704..e794f6d 100644 --- a/394 Decode String.py +++ b/394 Decode String.py @@ -112,7 +112,7 @@ def decodeString(self, s): if __name__ == "__main__": - print Solution().decodeString('2[abc]3[cd]ef') + assert Solution().decodeString('2[abc]3[cd]ef') == 'abcabccdcdcdef' From 014b420b9d81e9790bfc993449dad7eff493b153 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 6 Sep 2016 20:35:49 -0700 Subject: [PATCH 181/585] map --- 348 Design Tic-Tac-Toe.py | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 348 Design Tic-Tac-Toe.py diff --git a/348 Design Tic-Tac-Toe.py b/348 Design Tic-Tac-Toe.py new file mode 100644 index 0000000..5f2c47c --- /dev/null +++ b/348 Design Tic-Tac-Toe.py @@ -0,0 +1,53 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class TicTacToe(object): + def __init__(self, n): + """ + Initialize your data structure here. + :type n: int + """ + self.n = n + self.row = [0 for _ in xrange(n)] + self.col = [0 for _ in xrange(n)] + self.diag0 = 0 + self.diag1 = 0 + + def move(self, row, col, player): + """ + Since guarantee the move is valid, only store row, col, diagonal. + 1: -1 + 2: +1 + Player {player} makes a move at ({row}, {col}). + @param row The row of the board. + @param col The column of the board. + @param player The player, can be either 1 or 2. + @return The current winning condition, can be either: + 0: No one wins. + 1: Player 1 wins. + 2: Player 2 wins. + :type row: int + :type col: int + :type player: int + :rtype: int + """ + delta = -1 if player == 1 else 1 + self.col[col] += delta + self.row[row] += delta + if col == row: + self.diag0 += delta + if col + row == self.n - 1: + self.diag1 += delta + + is_win = lambda x: delta * x == self.n + if any(map(is_win, self.col)) or any(map(is_win, self.row)) or is_win(self.diag0) or is_win(self.diag1): + 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 From c0bad46ab0b9ba16cbb868d52c30c10e4d32c52c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 6 Sep 2016 20:50:38 -0700 Subject: [PATCH 182/585] reduce complexity --- 348 Design Tic-Tac-Toe.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/348 Design Tic-Tac-Toe.py b/348 Design Tic-Tac-Toe.py index 5f2c47c..0b1bb31 100644 --- a/348 Design Tic-Tac-Toe.py +++ b/348 Design Tic-Tac-Toe.py @@ -11,8 +11,8 @@ def __init__(self, n): :type n: int """ self.n = n - self.row = [0 for _ in xrange(n)] - self.col = [0 for _ in xrange(n)] + self.rows = [0 for _ in xrange(n)] + self.cols = [0 for _ in xrange(n)] self.diag0 = 0 self.diag1 = 0 @@ -35,15 +35,15 @@ def move(self, row, col, player): :rtype: int """ delta = -1 if player == 1 else 1 - self.col[col] += delta - self.row[row] += delta + self.cols[col] += delta + self.rows[row] += delta if col == row: self.diag0 += delta if col + row == self.n - 1: self.diag1 += delta is_win = lambda x: delta * x == self.n - if any(map(is_win, self.col)) or any(map(is_win, self.row)) or is_win(self.diag0) or is_win(self.diag1): + if any(map(is_win, [self.rows[row], self.cols[col], self.diag0, self.diag1])): return player return 0 From a0cf363eba324b4128e555642a4b299748fefbe1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 6 Sep 2016 22:09:45 -0700 Subject: [PATCH 183/585] BST info --- 333 Largest BST Subtree.py | 91 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 333 Largest BST Subtree.py diff --git a/333 Largest BST Subtree.py b/333 Largest BST Subtree.py new file mode 100644 index 0000000..6b220d1 --- /dev/null +++ b/333 Largest BST Subtree.py @@ -0,0 +1,91 @@ +""" +Premium Question +""" +import sys + +__author__ = 'Daniel' + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class BSTInfo(object): + def __init__(self, sz, lo, hi): + self.sz = sz + self.lo = lo + self.hi = hi + + +MAX = sys.maxint +MIN = -MAX - 1 + + +class Solution(object): + def __init__(self): + self.gmax = 0 + + def largestBSTSubtree(self, root): + """ + :type root: TreeNode + :rtype: int + """ + self.measure(root) + return self.gmax + + def measure(self, root): + if not root: + return BSTInfo(0, MAX, MIN) + + left = self.measure(root.left) + right = self.measure(root.right) + if left.sz == -1 or right.sz == -1 or not left.hi <= root.val or not root.val <= right.lo: + return BSTInfo(-1, MIN, MAX) + + sz = 1 + left.sz + right.sz + self.gmax = max(self.gmax, sz) + # when root.left is None + return BSTInfo(sz, min(root.val, left.lo), max(root.val, right.hi)) + + +class SolutionError(object): + def __init__(self): + self.gmax = 0 + + def largestBSTSubtree(self, root): + """ + :type root: TreeNode + :rtype: int + """ + self.measure(root) + return self.gmax + + def measure(self, root): + if not root: + return 0 + + left = self.measure(root.left) + right = self.measure(root.right) + + if root.left and not root.val >= root.left.val or root.right and not root.val <= root.right.val: + return 0 + + if root.left and left == 0 or root.right and right == 0: + return 0 + + ret = 1 + left + right + self.gmax = max(self.gmax, ret) + return ret + +if __name__ == "__main__": + root = TreeNode(1) + root.left = TreeNode(2) + print Solution().largestBSTSubtree(root) + + + + From 46322899d36383d4b81db5066b3a5e820c80ab5a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 8 Sep 2016 09:45:42 -0700 Subject: [PATCH 184/585] array/stack --- 388 Longest Absolute File Path.py | 67 +++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 388 Longest Absolute File Path.py diff --git a/388 Longest Absolute File Path.py b/388 Longest Absolute File Path.py new file mode 100644 index 0000000..5a6f38b --- /dev/null +++ b/388 Longest Absolute File Path.py @@ -0,0 +1,67 @@ +""" +Suppose we abstract our file system by a string in the following manner: + +The string "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext" represents: + +dir + subdir1 + subdir2 + file.ext +The directory dir contains an empty sub-directory subdir1 and a sub-directory subdir2 containing a file file.ext. + +The string "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext" represents: + +dir + subdir1 + file1.ext + subsubdir1 + subdir2 + subsubdir2 + file2.ext +The directory dir contains two sub-directories subdir1 and subdir2. subdir1 contains a file file1.ext and an empty +second-level sub-directory subsubdir1. subdir2 contains a second-level sub-directory subsubdir2 containing a file +file2.ext. + +We are interested in finding the longest (number of characters) absolute path to a file within our file system. For +example, in the second example above, the longest absolute path is "dir/subdir2/subsubdir2/file2.ext", and its length +is 32 (not including the double quotes). + +Given a string representing the file system in the above format, return the length of the longest absolute path to file +in the abstracted file system. If there is no file in the system, return 0. + +Note: +The name of a file contains at least a . and an extension. +The name of a directory or sub-directory will not contain a .. +Time complexity required: O(n) where n is the size of the input string. + +Notice that a/aa/aaa/file1.txt is not the longest file path, if there is another path aaaaaaaaaaaaaaaaaaaaa/sth.png. +""" +__author__ = 'Daniel' + + +class Solution(object): + def lengthLongestPath(self, input): + """ + :type input: str + :rtype: int + """ + input = input.split('\n') + F = [] + gmax = 0 + for elt in input: + idx = elt.count('\t') + idx = min(idx, len(F)) + e = elt.strip('\t') + prev = -1 if idx == 0 else F[idx-1] + if idx == len(F): + F.append(prev + 1 + len(e)) + else: + F[idx] = prev + 1 + len(e) # reset + + if '.' in elt: + gmax = max(gmax, F[idx]) + + return gmax + +if __name__ == "__main__": + assert Solution().lengthLongestPath("dir\n file.txt") == 12 From bb422ebd0538cf2d4028566c34f199c93ea71b17 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 8 Sep 2016 10:21:36 -0700 Subject: [PATCH 185/585] math --- 356 Line Reflection.py | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 356 Line Reflection.py diff --git a/356 Line Reflection.py b/356 Line Reflection.py new file mode 100644 index 0000000..f4b953b --- /dev/null +++ b/356 Line Reflection.py @@ -0,0 +1,51 @@ +""" +Premium Question +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class Solution(object): + def __init__(self): + self.x = None + + def isReflected(self, points): + """ + :type points: List[List[int]] + :rtype: bool + """ + d = defaultdict(list) + for x, y in points: + d[y].append(x) + + for v in d.values(): + if not self.check(v): + return False + + return True + + def check(self, lst): + lst.sort() + i = 0 + j = len(lst) - 1 + while i < j: + x = (lst[i] + lst[j]) / float(2) + if not self.x: + self.x = x + elif self.x != x: + return False + + i += 1 + j -= 1 + + if i == j: + if not self.x: + self.x = lst[i] + elif self.x != lst[i]: + return False + + return True + +if __name__ == "__main__": + assert Solution().isReflected([[1,1],[-1,-1]]) == False From d94406e7d608c3a0a1dc24b87c5a28046a3e5350 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 8 Sep 2016 14:59:24 -0700 Subject: [PATCH 186/585] np dfs --- 351 Android Unlock Patterns.py | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 351 Android Unlock Patterns.py diff --git a/351 Android Unlock Patterns.py b/351 Android Unlock Patterns.py new file mode 100644 index 0000000..bcb893a --- /dev/null +++ b/351 Android Unlock Patterns.py @@ -0,0 +1,61 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution(object): + def __init__(self): + """ + encode rules + """ + self.skip = [[None for _ in xrange(10)] for _ in xrange(10)] + self.skip[1][3], self.skip[3][1] = 2, 2 + self.skip[1][7], self.skip[7][1] = 4, 4 + self.skip[3][9], self.skip[9][3] = 6, 6 + self.skip[7][9], self.skip[9][7] = 8, 8 + self.skip[4][6], self.skip[6][4] = 5, 5 + self.skip[2][8], self.skip[8][2] = 5, 5 + self.skip[1][9], self.skip[9][1] = 5, 5 + self.skip[3][7], self.skip[7][3] = 5, 5 + + def numberOfPatterns(self, m, n): + """ + NP + dfs + + Maintain a skip matrix + :type m: int + :type n: int + :rtype: int + """ + visited = [False for _ in xrange(10)] + return sum( + self.dfs(1, visited, remain) * 4 + + self.dfs(2, visited, remain) * 4 + + self.dfs(5, visited, remain) + for remain in xrange(m, n+1) + ) + + def dfs(self, cur, visited, remain): + if remain == 1: + return 1 + + visited[cur] = True + ret = 0 + for nxt in xrange(1, 10): + if ( + not visited[nxt] and ( + self.skip[cur][nxt] is None or + visited[self.skip[cur][nxt]] + ) + ): + ret += self.dfs(nxt, visited, remain - 1) + + visited[cur] = False + return ret + + +if __name__ == "__main__": + assert Solution().numberOfPatterns(1, 2) == 65 + assert Solution().numberOfPatterns(1, 3) == 385 \ No newline at end of file From 9da17c621885b6a2e28ada76458ae15e14f5c9d1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 23 Sep 2016 16:08:57 -0700 Subject: [PATCH 187/585] prettify --- 042 Multiply Strings.py | 66 ++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/042 Multiply Strings.py b/042 Multiply Strings.py index 818445a..8cde4ea 100644 --- a/042 Multiply Strings.py +++ b/042 Multiply Strings.py @@ -28,22 +28,21 @@ def multiply(self, num1, num2): # pre processing if len(num1) < len(num2): # order them first - num1, num2 = num2, num1 + return self.multiply(num2, num1) num2 = num2[::-1] # reverse them first num1 = num1[::-1] # multiply by 1 digit at a time - for i in range(len(num2)): - result.append(self.multiply_1_digit(num2[i], num1)) + for char in num2: + result.append(self.multiply_1_digit(char, num1)) # add the temporary results up lst = self.add_list(result) - # post processing lst.reverse() # reverse back - result = "".join(str(item) for item in lst).lstrip("0") + result = "".join(map(str, lst)).lstrip("0") if not result: return "0" return result @@ -55,20 +54,20 @@ def multiply_1_digit(self, digit, num): :return: list of digit in reverse order """ digit = int(digit) - temp = [int(item) for item in num] + ret = [] carry = 0 - for ind in range(len(temp)): - val = temp[ind] - mul = val*digit+carry + for elt in num: + val = int(elt) + mul = val*digit + carry carry = mul/10 - mul = mul%10 - temp[ind] = mul + mul %= 10 + ret.append(mul) if carry != 0: - temp.append(carry) + ret.append(carry) - return temp + return ret def add_list(self, lst): """ @@ -76,14 +75,14 @@ def add_list(self, lst): :param lst: :return: """ - appending_zero = 0 - result = [0] + sig = 0 + ret = [0] for ind, val in enumerate(lst): - for i in range(appending_zero): + for i in range(sig): val.insert(0, 0) # NOTICE: side-effect - result = self.add(result, val) - appending_zero += 1 - return result + ret = self.add(ret, val) + sig += 1 + return ret def add(self, num1, num2): """ @@ -91,32 +90,31 @@ def add(self, num1, num2): :param num2: list of digits in reverse order :return: list of digits in reverse order """ - num1 = list(num1) # NOTICE: local copy - num2 = list(num2) # NOTICE: local copy + if len(num1) < len(num2): - num1, num2 = num2, num1 + return self.add(num2, num1) + ret = [] carry = 0 - for ind in range(len(num1)): # longer one + for idx in xrange(len(num1)): # longer one try: - result = num1[ind]+num2[ind]+carry + sm = num1[idx]+num2[idx]+carry except IndexError: - result = num1[ind]+carry - if result == num1[ind]: break # prune + sm = num1[idx] + carry - carry = result/10 - num1[ind] = result%10 + carry = sm/10 + ret.append(sm % 10) if carry != 0: - num1.append(carry) + ret.append(carry) - return num1 + return ret if __name__ == "__main__": solution = Solution() - assert [1, 2]==solution.add([2,1], [9]) - assert [1, 9, 9, 8]==solution.multiply_1_digit("9", "999") - assert str(123*999)==solution.multiply("123", "999") - assert str(0)==solution.multiply("0", "0") + assert [1, 2] == solution.add([2, 1], [9]) + assert [1, 9, 9, 8] == solution.multiply_1_digit("9", "999") + assert str(123*999) == solution.multiply("123", "999") + assert str(0) == solution.multiply("0", "0") assert str(123*456) == solution.multiply("123", "456") From bc23a0accbd9ee16b79f6c6e98d156d503f2ac84 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 24 Sep 2016 13:19:44 -0700 Subject: [PATCH 188/585] prettify --- 042 Multiply Strings.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/042 Multiply Strings.py b/042 Multiply Strings.py index 8cde4ea..81fcaf2 100644 --- a/042 Multiply Strings.py +++ b/042 Multiply Strings.py @@ -27,15 +27,16 @@ def multiply(self, num1, num2): result = [] # pre processing - if len(num1) < len(num2): # order them first + if len(num1) > len(num2): # order them first return self.multiply(num2, num1) - num2 = num2[::-1] # reverse them first - num1 = num1[::-1] + # reverse them first + num1 = map(int, list(num1[::-1])) + num2 = map(int, list(num2[::-1])) # multiply by 1 digit at a time - for char in num2: - result.append(self.multiply_1_digit(char, num1)) + for d in num1: + result.append(self.multiply_1_digit(d, num2)) # add the temporary results up lst = self.add_list(result) @@ -53,13 +54,11 @@ def multiply_1_digit(self, digit, num): :param num: String :return: list of digit in reverse order """ - digit = int(digit) ret = [] carry = 0 for elt in num: - val = int(elt) - mul = val*digit + carry + mul = elt*digit + carry carry = mul/10 mul %= 10 ret.append(mul) @@ -78,8 +77,7 @@ def add_list(self, lst): sig = 0 ret = [0] for ind, val in enumerate(lst): - for i in range(sig): - val.insert(0, 0) # NOTICE: side-effect + for i in xrange(sig): val.insert(0, 0) # possible deque ret = self.add(ret, val) sig += 1 return ret @@ -91,16 +89,16 @@ def add(self, num1, num2): :return: list of digits in reverse order """ - if len(num1) < len(num2): + if len(num1) > len(num2): return self.add(num2, num1) ret = [] carry = 0 - for idx in xrange(len(num1)): # longer one + for idx in xrange(len(num2)): # longer one try: - sm = num1[idx]+num2[idx]+carry + sm = num1[idx] + num2[idx] + carry except IndexError: - sm = num1[idx] + carry + sm = num2[idx] + carry carry = sm/10 ret.append(sm % 10) @@ -114,7 +112,6 @@ def add(self, num1, num2): if __name__ == "__main__": solution = Solution() assert [1, 2] == solution.add([2, 1], [9]) - assert [1, 9, 9, 8] == solution.multiply_1_digit("9", "999") assert str(123*999) == solution.multiply("123", "999") assert str(0) == solution.multiply("0", "0") assert str(123*456) == solution.multiply("123", "456") From 42c70b802f23c60d9e740e007cc80af784e0eb45 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 25 Sep 2016 11:10:41 -0700 Subject: [PATCH 189/585] dp --- 361 Bomb Enemy.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 361 Bomb Enemy.py diff --git a/361 Bomb Enemy.py b/361 Bomb Enemy.py new file mode 100644 index 0000000..38ba321 --- /dev/null +++ b/361 Bomb Enemy.py @@ -0,0 +1,61 @@ +""" +Given a 2D grid, each cell is either a wall 'W', an enemy 'E' or empty '0' (the number zero), return the maximum enemies +you can kill using one bomb. +The bomb kills all the enemies in the same row and column from the planted point until it hits the wall since the wall +is too strong to be destroyed. +Note that you can only put the bomb at an empty cell. + +Example: +For the given grid + +0 E 0 0 +E 0 W E +0 E 0 0 + +return 3. (Placing a bomb at (1,1) kills 3 enemies) +""" +__author__ = 'Daniel' + + +class Solution(object): + def maxKilledEnemies(self, grid): + """ + Brute force: O(n * n^2) + Place the bomb around boundary + The result of boundary bomb can be reused - dp + + The time complexity is O(m + n) + :type grid: List[List[str]] + :rtype: int + """ + if not grid: return 0 + + m, n = len(grid), len(grid[0]) + rows = [0 for _ in xrange(m)] + cols = [0 for _ in xrange(n)] + gmax = 0 + for i in xrange(m): + for j in xrange(n): + if i == 0 or grid[i-1][j] == 'W': + cols[j] = 0 + for k in xrange(i, m): + if grid[k][j] == 'E': + cols[j] += 1 + elif grid[k][j] == 'W': + break + + if j == 0 or grid[i][j-1] == 'W': + rows[i] = 0 + for k in xrange(j, n): + if grid[i][k] == 'E': + rows[i] += 1 + elif grid[i][k] == 'W': + break + + if grid[i][j] == '0': + gmax = max(gmax, rows[i] + cols[j]) + + return gmax + +if __name__ == "__main__": + assert Solution().maxKilledEnemies(["0E00", "E0WE", "0E00"]) == 3 \ No newline at end of file From 0929c0e9b42389e616be3990434dc28d136bfc08 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 25 Sep 2016 11:10:50 -0700 Subject: [PATCH 190/585] update --- 310 Minimum Height Trees.py | 13 +++++++------ 324 Wiggle Sort II.py | 21 ++++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/310 Minimum Height Trees.py b/310 Minimum Height Trees.py index af29b8f..96f8120 100644 --- a/310 Minimum Height Trees.py +++ b/310 Minimum Height Trees.py @@ -66,14 +66,14 @@ def findMinHeightTrees(self, n, edges): cur = pi[cur] ret.append(cur) - if level % 2 == 0: + if level%2 == 0: ret.append(pi[cur]) return ret def bfs(self, s, V): # bfs - visisted = [False for _ in xrange(len(V))] + visited = [False for _ in xrange(len(V))] pi = [-1 for _ in xrange(len(V))] last = s level = 0 @@ -84,9 +84,9 @@ def bfs(self, s, V): for i in xrange(l): cur = q[i] last = cur - visisted[cur] = True + visited[cur] = True for nbr in V[cur]: - if not visisted[nbr]: + if not visited[nbr]: pi[nbr] = cur q.append(nbr) @@ -185,11 +185,12 @@ def findMinHeightTrees(self, n, edges): q = q[l:] level += 1 - if level % 2 == 0: + if level%2 == 0: return h2v[level/2-1]+h2v[level/2] else: return h2v[level/2] + if __name__ == "__main__": # print Solution().findMinHeightTrees(6, [[3,0],[3,1],[3,2],[3,4],[5,4]]) - print Solution().findMinHeightTrees(7, [[0,1],[1,2],[1,3],[2,4],[3,5],[4,6]]) \ No newline at end of file + assert Solution().findMinHeightTrees(7, [[0, 1], [1, 2], [1, 3], [2, 4], [3, 5], [4, 6]]) == [1, 2] \ No newline at end of file diff --git a/324 Wiggle Sort II.py b/324 Wiggle Sort II.py index dac3e27..6dead5c 100644 --- a/324 Wiggle Sort II.py +++ b/324 Wiggle Sort II.py @@ -29,22 +29,20 @@ def wiggleSort(self, A): median_idx = self.find_kth(A, 0, n, n/2) v = A[median_idx] - def idx(i): - return (2*i+1) % (n|1) - - l = -1 - r = n + idx = lambda i: (2*i+1)%(n|1) + lt = -1 + hi = n i = 0 - while i < r: + while i < hi: if A[idx(i)] > v: - l += 1 - A[idx(l)], A[idx(i)] = A[idx(i)], A[idx(l)] + lt += 1 + A[idx(lt)], A[idx(i)] = A[idx(i)], A[idx(lt)] i += 1 elif A[idx(i)] == v: i += 1 else: - r -= 1 - A[idx(r)], A[idx(i)] = A[idx(i)], A[idx(r)] + hi -= 1 + A[idx(hi)], A[idx(i)] = A[idx(i)], A[idx(hi)] def pivot(self, A, lo, hi, pidx=None): lt = lo-1 @@ -70,6 +68,7 @@ def find_kth(self, A, lo, hi, k): if lo >= hi: return lt, gt = self.pivot(A, lo, hi) + if lt < k < gt: return k if k <= lt: @@ -105,7 +104,7 @@ def wiggleSort(self, nums): if __name__ == "__main__": # A = [1, 5, 1, 1, 6, 4] - A = [3,2,1,1,3,2] + A = [3, 2, 1, 1, 3, 2] Solution().wiggleSort(A) print A From cc7a82dd818d33d0a33779a4cdfc6f39220ebf9e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 25 Sep 2016 11:40:39 -0700 Subject: [PATCH 191/585] segment tree --- 406 Queue Reconstruction by Height.py | 98 +++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 406 Queue Reconstruction by Height.py diff --git a/406 Queue Reconstruction by Height.py b/406 Queue Reconstruction by Height.py new file mode 100644 index 0000000..a52ea7f --- /dev/null +++ b/406 Queue Reconstruction by Height.py @@ -0,0 +1,98 @@ +""" +Suppose you have a random list of people standing in a queue. Each person is described by a pair of integers (h, k), +where h is the height of the person and k is the number of people in front of this person who have a height greater +than or equal to h. Write an algorithm to reconstruct the queue. + +Note: +The number of people is less than 1,100. + +Example + +Input: +[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]] + +Output: +[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]] +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class Node(object): + def __init__(self, lo, hi, cnt): + self.lo = lo + self.hi = hi + self.cnt = cnt # size of empty slots + + self.left = None + self.right = None + + def __repr__(self): + return repr("[%d,%d)" % (self.lo, self.hi)) + + +class SegmentTree(object): + """empty space""" + + def __init__(self): + self.root = None + + def build(self, lo, hi): + """a node can have right ONLY IF has left""" + if lo >= hi: return + if lo == hi-1: return Node(lo, hi, 1) + + root = Node(lo, hi, hi-lo) + root.left = self.build(lo, (hi+lo)/2) + root.right = self.build((lo+hi)/2, hi) + return root + + def find_delete(self, root, sz): + """ + :return: index + """ + root.cnt -= 1 + if not root.left: + return root.lo + elif root.left.cnt >= sz: + return self.find_delete(root.left, sz) + else: + return self.find_delete(root.right, + sz-root.left.cnt) + + +class Solution(object): + def reconstructQueue(self, A): + """ + :type A: List[List[int]] + :rtype: List[List[int]] + """ + + def cmp(a, b): + if a[0] != b[0]: + return a[0]-b[0] + else: + return a[1]-b[1] + + st = SegmentTree() + n = len(A) + st.root = st.build(0, n) + A.sort(cmp=cmp) + ret = [0]*n + ret_cnt = defaultdict(int) # handle duplicate element + for a in A: + val, inv = a + idx = st.find_delete(st.root, inv+1-ret_cnt[val]) + ret_cnt[val] += 1 + ret[idx] = a + + return ret + + +if __name__ == "__main__": + assert Solution().reconstructQueue( + [[9, 0], [7, 0], [1, 9], [3, 0], [2, 7], [5, 3], [6, 0], [3, 4], [6, 2], [5, 2]]) == [[3, 0], [6, 0], [7, 0], + [5, 2], [3, 4], [5, 3], + [6, 2], [2, 7], [9, 0], + [1, 9]] \ No newline at end of file From c239dc4af692807e5768333a761e90426af7dcd5 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 26 Sep 2016 10:45:29 -0700 Subject: [PATCH 192/585] graph dfs --- 399 Evaluate Division.py | 83 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 399 Evaluate Division.py diff --git a/399 Evaluate Division.py b/399 Evaluate Division.py new file mode 100644 index 0000000..8249d44 --- /dev/null +++ b/399 Evaluate Division.py @@ -0,0 +1,83 @@ +""" +Equations are given in the format A / B = k, where A and B are variables represented as strings, and k is a real number +(floating point number). Given some queries, return the answers. If the answer does not exist, return -1.0. + +Example: +Given a / b = 2.0, b / c = 3.0. +queries are: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? . +return [6.0, 0.5, -1.0, 1.0, -1.0 ]. + +The input is: vector> equations, vector& values, vector> queries , +where equations.size() == values.size(), and the values are positive. This represents the equations. Return +vector. + +According to the example above: + +equations = [ ["a", "b"], ["b", "c"] ], +values = [2.0, 3.0], +queries = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ]. + +""" +from collections import defaultdict +from itertools import izip + +__author__ = 'Daniel' + + +class Solution(object): + def calcEquation(self, equations, values, queries): + """ + transitive closure + :type equations: List[List[str]] + :type values: List[float] + :type queries: List[List[str]] + :rtype: List[float] + """ + G = defaultdict(dict) + for edge, val in izip(equations, values): + s, e = edge + G[s][e], G[e][s] = val, 1/val + G[s][s], G[e][e] = 1, 1 + + return [self.dfs(G, s, e, set()) for s, e in queries] + + def dfs(self, G, s, e, path): + if s not in G or e not in G: + return -1.0 + if e in G[s]: + return G[s][e] + for nbr in G[s]: + if nbr not in path: + path.add(nbr) + val = self.dfs(G, nbr, e, path) + if val != -1.0: + return val * G[s][nbr] + path.remove(nbr) + + return -1.0 + + +class Solution(object): + def calcEquation(self, equations, values, queries): + """ + Floyd-Warshall algorithm + transitive closure + :type equations: List[List[str]] + :type values: List[float] + :type queries: List[List[str]] + :rtype: List[float] + """ + G = defaultdict(dict) + for edge, val in izip(equations, values): + s, e = edge + G[s][e], G[e][s] = val, 1/val + G[s][s], G[e][e] = 1, 1 + + # Floyd-Warshall + for mid in G: + for s in G[mid]: + for e in G[mid]: + G[s][e] = G[s][mid] * G[mid][e] + + return [G[s].get(e, -1.0) for s, e in queries] + From 1ca1bbf22ee4443c6e8fb3212777a755b7d9b440 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 26 Sep 2016 11:46:43 -0700 Subject: [PATCH 193/585] Reservoir Sampling --- 398 Random Pick Index.py | 74 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 398 Random Pick Index.py diff --git a/398 Random Pick Index.py b/398 Random Pick Index.py new file mode 100644 index 0000000..f43a4a0 --- /dev/null +++ b/398 Random Pick Index.py @@ -0,0 +1,74 @@ +""" +Given an array of integers with possible duplicates, randomly output the index of a given target number. You can assume +that the given target number must exist in the array. + +Note: +The array size can be very large. Solution that uses too much extra space will not pass the judge. + +Example: + +int[] nums = new int[] {1,2,3,3,3}; +Solution solution = new Solution(nums); + +// pick(3) should return either index 2, 3, or 4 randomly. Each index should have equal probability of returning. +solution.pick(3); + +// pick(1) should return 0. Since in the array only nums[0] is equal to 1. +solution.pick(1); +""" +import random + +__author__ = 'Daniel' + + +class Solution(object): + def __init__(self, nums): + """ + :type nums: List[int] + """ + self.A = nums + + def pick(self, target): + """ + O(n) + Reservoir Sampling + :type target: int + :rtype: int + """ + sz = 0 + ret = None + for idx, val in enumerate(self.A): + if val == target: + sz += 1 + p = random.randrange(0, sz) + if p == 0: + ret = idx + + return ret + + +class SolutionError(object): + def __init__(self, nums): + """ + Reservoir Sampling + Assume pick is only called once + :type nums: List[int] + """ + self.d = {} + for idx, val in enumerate(nums): + if val not in self.d: + self.d[val] = (idx, 1) + else: + prev, sz = self.d[val] + p = random.randrange(0, sz) + if p < sz: + self.d[val] = (idx, sz + 1) + else: + self.d[val] = (prev, sz + 1) + + def pick(self, target): + """ + :type target: int + :rtype: int + """ + return self.d[target][0] \ No newline at end of file From 1c82144675188878e438d7e3d4d4d4b606a187da Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 26 Sep 2016 12:43:40 -0700 Subject: [PATCH 194/585] Stack and greedy --- 402 Remove K Digits.py | 46 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 402 Remove K Digits.py diff --git a/402 Remove K Digits.py b/402 Remove K Digits.py new file mode 100644 index 0000000..8203f76 --- /dev/null +++ b/402 Remove K Digits.py @@ -0,0 +1,46 @@ +""" +Given a non-negative integer num represented as a string, remove k digits from the number so that the new number is the +smallest possible. + +Note: +The length of num is less than 10002 and will be >= k. +The given num does not contain any leading zero. +Example 1: + +Input: num = "1432219", k = 3 +Output: "1219" +Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest. +Example 2: + +Input: num = "10200", k = 1 +Output: "200" +Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes. +Example 3: + +Input: num = "10", k = 2 +Output: "0" +Explanation: Remove all the digits from the number and it is left with nothing which is 0. +""" +__author__ = 'Daniel' + + +class Solution(object): + def removeKdigits(self, num, k): + """ + Stack and greedy. + The leading digits should be as small as possible + :type num: str + :type k: int + :rtype: str + """ + stk = [] # result after removal + for char in num: + while k and stk and stk[-1] > char: + stk.pop() + k -= 1 + + stk.append(char) + + for _ in xrange(k): stk.pop() + + return ''.join(stk).lstrip('0') or '0' From 62678d278081907acaa616799ec5ccd569a90c1a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 27 Sep 2016 08:54:06 -0700 Subject: [PATCH 195/585] pool of numbers --- 379 Design Phone Directory.py | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 379 Design Phone Directory.py diff --git a/379 Design Phone Directory.py b/379 Design Phone Directory.py new file mode 100644 index 0000000..e384577 --- /dev/null +++ b/379 Design Phone Directory.py @@ -0,0 +1,64 @@ +""" +Design a Phone Directory which supports the following operations: + +get: Provide a number which is not assigned to anyone. +check: Check if a number is available or not. +release: Recycle or release a number. +""" +__author__ = 'Daniel' + + +class PhoneDirectory(object): + def __init__(self, maxNumbers): + """ + Pool of numbers + Two parts: + 1. set + 2. range pointers to save memory + @param maxNumbers - The maximum numbers that can be stored in the phone directory. + :type maxNumbers: int + """ + self.released = set() + self.l = maxNumbers + self.i = 0 + + def get(self): + """ + Provide a number which is not assigned to anyone. + @return - Return an available number. Return -1 if none is available. + :rtype: int + """ + if self.released: + return self.released.pop() + if self.i < self.l: + ret = self.i + self.i += 1 + return ret + + return -1 + + def check(self, number): + """ + Check if a number is available or not. + :type number: int + :rtype: bool + """ + return number in self.released or self.i <= number < self.l + + def release(self, number): + """ + Recycle or release a number. + :type number: int + :rtype: void + """ + if self.i <= number < self.l: + return + + self.released.add(number) + + +# Your PhoneDirectory object will be instantiated and called as such: +# obj = PhoneDirectory(maxNumbers) +# param_1 = obj.get() +# param_2 = obj.check(number) +# obj.release(number) \ No newline at end of file From d07f9befc93b0acb11728aee6bfd16e59ed37612 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 27 Sep 2016 09:22:50 -0700 Subject: [PATCH 196/585] game design --- 353 Design Snake Game.py | 67 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 353 Design Snake Game.py diff --git a/353 Design Snake Game.py b/353 Design Snake Game.py new file mode 100644 index 0000000..eb03cfd --- /dev/null +++ b/353 Design Snake Game.py @@ -0,0 +1,67 @@ +""" +Design a Snake game that is played on a device with screen size = width x height. +""" +from collections import deque + +__author__ = 'Daniel' + + +class SnakeGame(object): + def __init__(self, width, height, food): + """ + Initialize your data structure here. + @param width - screen width + @param height - screen height + @param food - A list of food positions + E.g food = [[1,1], [1,0]] means the first food is positioned at [1,1], the second is at [1,0]. + :type width: int + :type height: int + :type food: List[List[int]] + """ + self.w = width + self.h = height + self.food = deque(food) + self.body = deque([(0, 0)]) + self.dirs = { + 'U': (-1, 0), + 'L': (0, -1), + 'R': (0, 1), + 'D': (1, 0), + } + self.eat = 0 + + def move(self, direction): + """ + Moves the snake. + @param direction - 'U' = Up, 'L' = Left, 'R' = Right, 'D' = Down + @return The game's score after the move. Return -1 if game over. + Game over when snake crosses the screen boundary or bites its body. + :type direction: str + :rtype: int + """ + x, y = self.body[0] + dx, dy = self.dirs[direction] + x += dx + y += dy + fx, fy = self.food[0] if self.food else (-1, -1) + if x == fx and y == fy: + self.food.popleft() + self.eat += 1 + else: + self.body.pop() + if (x, y) in self.body or not (0 <= x < self.h and 0 <= y < self.w): + # possible to use set to accelerate check + return -1 + + self.body.appendleft((x, y)) + return self.eat + + +# Your SnakeGame object will be instantiated and called as such: +# obj = SnakeGame(width, height, food) +# param_1 = obj.move(direction) + +if __name__ == "__main__": + game = SnakeGame(3, 2, [[1, 2], [0, 1]]) + for char, expect in zip('RDRULU', [0, 0, 1, 1, 2, -1]): + assert game.move(char) == expect From f975d2e17462ccd2bf9145526b5af7d34995dafe Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 27 Sep 2016 10:28:02 -0700 Subject: [PATCH 197/585] twitter, heap for news feed ranking --- 355 Design Twitter.py | 137 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 355 Design Twitter.py diff --git a/355 Design Twitter.py b/355 Design Twitter.py new file mode 100644 index 0000000..56bab26 --- /dev/null +++ b/355 Design Twitter.py @@ -0,0 +1,137 @@ +""" +Design a simplified version of Twitter where users can post tweets, follow/unfollow another user and is able to see the +10 most recent tweets in the user's news feed. Your design should support the following methods: + +postTweet(userId, tweetId): Compose a new tweet. +getNewsFeed(userId): Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be +posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. +follow(followerId, followeeId): Follower follows a followee. +unfollow(followerId, followeeId): Follower unfollows a followee. +Example: + +Twitter twitter = new Twitter(); + +// User 1 posts a new tweet (id = 5). +twitter.postTweet(1, 5); + +// User 1's news feed should return a list with 1 tweet id -> [5]. +twitter.getNewsFeed(1); + +// User 1 follows user 2. +twitter.follow(1, 2); + +// User 2 posts a new tweet (id = 6). +twitter.postTweet(2, 6); + +// User 1's news feed should return a list with 2 tweet ids -> [6, 5]. +// Tweet id 6 should precede tweet id 5 because it is posted after tweet id 5. +twitter.getNewsFeed(1); + +// User 1 unfollows user 2. +twitter.unfollow(1, 2); + +// User 1's news feed should return a list with 1 tweet id -> [5], +// since user 1 is no longer following user 2. +twitter.getNewsFeed(1); +""" +from collections import defaultdict +import heapq + +__author__ = 'Daniel' + + +SZ = 10 + + +class Tweet(object): + central_clk = 0 + + def __init__(self, id, nxt=None): + self.timestamp = Tweet.central_clk + self.id = id + self.next = nxt # LinkedList + Tweet.central_clk += 1 + + def __cmp__(self, other): + return - (self.timestamp - other.timestamp) + + +class Twitter(object): + """ + need assumption about the frequency of calls of each method + + most efficient heap of list + """ + def __init__(self): + """ + Initialize your data structure here. + """ + self.tweets = defaultdict(lambda: None) + self.followees = defaultdict(set) + + def postTweet(self, userId, tweetId): + """ + Compose a new tweet. + :type userId: int + :type tweetId: int + :rtype: void + """ + nxt = self.tweets[userId] # previous post + self.tweets[userId] = Tweet(tweetId, nxt) + + def getNewsFeed(self, userId): + """ + Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by + users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. + :type userId: int + :rtype: List[int] + """ + h = [] + if userId not in self.followees[userId] and self.tweets[userId]: + # possible following oneself + heapq.heappush(h, self.tweets[userId]) + + for followee in self.followees[userId]: + if self.tweets[followee]: + heapq.heappush(h, self.tweets[followee]) + + ret = [] + while h and len(ret) < SZ: + tweet = heapq.heappop(h) + ret.append(tweet.id) + if tweet.next: + heapq.heappush(h, tweet.next) + + return ret + + def follow(self, followerId, followeeId): + """ + Follower follows a followee. If the operation is invalid, it should be a no-op. + :type followerId: int + :type followeeId: int + :rtype: void + """ + self.followees[followerId].add(followeeId) + + def unfollow(self, followerId, followeeId): + """ + Follower unfollows a followee. If the operation is invalid, it should be a no-op. + :type followerId: int + :type followeeId: int + :rtype: void + """ + self.followees[followerId].discard(followeeId) # no KeyError compared to .remove() + + +# Your Twitter object will be instantiated and called as such: +# obj = Twitter() +# obj.postTweet(userId,tweetId) +# param_2 = obj.getNewsFeed(userId) +# obj.follow(followerId,followeeId) +# obj.unfollow(followerId,followeeId) + + +if __name__ == "__main__": + twitter = Twitter() + twitter.postTweet(1, 5) + twitter.unfollow(1, 1) From 19036db5f14b0927a9526b1d7059a11194712220 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 27 Sep 2016 20:44:41 -0700 Subject: [PATCH 198/585] pattern --- 390 Elimination Game.py | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 390 Elimination Game.py diff --git a/390 Elimination Game.py b/390 Elimination Game.py new file mode 100644 index 0000000..6892255 --- /dev/null +++ b/390 Elimination Game.py @@ -0,0 +1,50 @@ +""" +There is a list of sorted integers from 1 to n. Starting from left to right, remove the first number and every other +number afterward until you reach the end of the list. + +Repeat the previous step again, but this time from right to left, remove the right most number and every other number +from the remaining numbers. + +We keep repeating the steps again, alternating left to right and right to left, until a single number remains. + +Find the last number that remains starting with a list of length n. + +Example: + +Input: +n = 9, +1 2 3 4 5 6 7 8 9 +2 4 6 8 +2 6 +6 + +Output: +6 +""" +__author__ = 'Daniel' + + +class Solution(object): + def lastRemaining(self, n): + """ + Brute force O(n): A = A[::2][::-1] + + Simulate the game and find pattern of head/first element: O(lg n) + :type n: int + :rtype: int + """ + remain = n + head = 1 + step = 1 + from_left = True + while remain > 1: + if from_left: + head += step + elif remain % 2 == 1: + head += step + + step *= 2 + remain /= 2 + from_left = not from_left + + return head From 055552d80c73ade093569aa0a1be578432c43c47 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 27 Sep 2016 21:16:59 -0700 Subject: [PATCH 199/585] bit manipulation --- 393 UTF-8 Validation.py | 74 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 393 UTF-8 Validation.py diff --git a/393 UTF-8 Validation.py b/393 UTF-8 Validation.py new file mode 100644 index 0000000..2e57683 --- /dev/null +++ b/393 UTF-8 Validation.py @@ -0,0 +1,74 @@ +""" +A character in UTF8 can be from 1 to 4 bytes long, subjected to the following rules: + +For 1-byte character, the first bit is a 0, followed by its unicode code. +For n-bytes character, the first n-bits are all one's, the n+1 bit is 0, followed by n-1 bytes with most significant 2 +bits being 10. +This is how the UTF-8 encoding would work: + + Char. number range | UTF-8 octet sequence + (hexadecimal) | (binary) + --------------------+--------------------------------------------- + 0000 0000-0000 007F | 0xxxxxxx + 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx +Given an array of integers representing the data, return whether it is a valid utf-8 encoding. + +Note: +The input is an array of integers. Only the least significant 8 bits of each integer is used to store the data. This +means each integer represents only 1 byte of data. + +Example 1: + +data = [197, 130, 1], which represents the octet sequence: 11000101 10000010 00000001. + +Return true. +It is a valid utf-8 encoding for a 2-bytes character followed by a 1-byte character. +Example 2: + +data = [235, 140, 4], which represented the octet sequence: 11101011 10001100 00000100. + +Return false. +The first 3 bits are all one's and the 4th bit is 0 means it is a 3-bytes character. +The next byte is a continuation byte which starts with 10 and that's correct. +But the second continuation byte does not start with 10, so it is invalid. +""" + +__author__ = 'Daniel' + + +class Solution(object): + def validUtf8(self, data): + """ + starts with 0, then skip + start with 1, check number of numbers + :type data: List[int] + :rtype: bool + """ + required = 0 + for d in data: + if d & 0x80 == 0: + if required != 0: + return False + else: + one_cnt = 0 + while d & 0x80 == 0x80: + one_cnt += 1 + d <<= 1 + + if required != 0: + if one_cnt != 1: + return False + required -= 1 + else: + if one_cnt == 1: + return False + required += (one_cnt - 1) + + return required == 0 + + +if __name__ == "__main__": + assert Solution().validUtf8([197, 130, 1]) == True + assert Solution().validUtf8([235, 140, 4]) == False From c4d9b39113e4fab260f7c3a2051c888444b3b0b1 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 27 Sep 2016 21:49:17 -0700 Subject: [PATCH 200/585] tree --- 404 Sum of Left Leaves.py | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 404 Sum of Left Leaves.py diff --git a/404 Sum of Left Leaves.py b/404 Sum of Left Leaves.py new file mode 100644 index 0000000..e617757 --- /dev/null +++ b/404 Sum of Left Leaves.py @@ -0,0 +1,45 @@ +""" +Find the sum of all left leaves in a given binary tree. + +Example: + + 3 + / \ + 9 20 + / \ + 15 7 + +There are two left leaves in the binary tree, with values 9 and 15 respectively. Return 24. +""" +__author__ = 'Daniel' + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def __init__(self): + self.s = 0 + + def sumOfLeftLeaves(self, root): + """ + :type root: TreeNode + :rtype: int + """ + self.traverse(root) + return self.s + + def traverse(self, node): + if not node: + return + + if node.left and not node.left.left and not node.left.right: + self.s += node.left.val + + self.traverse(node.left) + self.traverse(node.right) From ec5f822d5323238d26b958089441f483940d7cde Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 27 Sep 2016 22:39:15 -0700 Subject: [PATCH 201/585] orderly backtracking --- 401 Binary Watch.py | 73 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 401 Binary Watch.py diff --git a/401 Binary Watch.py b/401 Binary Watch.py new file mode 100644 index 0000000..2da0e2d --- /dev/null +++ b/401 Binary Watch.py @@ -0,0 +1,73 @@ +""" +A binary watch has 4 LEDs on the top which represent the hours (0-11), and the 6 LEDs on the bottom represent the +minutes (0-59). + +Each LED represents a zero or one, with the least significant bit on the right. + + +For example, the above binary watch reads "3:25". + +Given a non-negative integer n which represents the number of LEDs that are currently on, return all possible times the +watch could represent. + +Example: + +Input: n = 1 +Return: ["1:00", "2:00", "4:00", "8:00", "0:01", "0:02", "0:04", "0:08", "0:16", "0:32"] +Note: +The order of output does not matter. +The hour must not contain a leading zero, for example "01:00" is not valid, it should be "1:00". +The minute must be consist of two digits and may contain a leading zero, for example "10:2" is not valid, it should be +"10:02". +""" +__author__ = 'Daniel' + + +class Solution(object): + def __init__(self): + self.hours = (1, 2, 4, 8) + self.minutes = (1, 2, 4, 8, 16, 32) + + def readBinaryWatch(self, num): + """ + orderly backtracking + + :type num: int + :rtype: List[str] + """ + def gen(): + for hour_n in xrange(min(num, 4)+1): + for hour in self.hour(hour_n): + for minute in self.minute(num-hour_n): + hour = str(hour) + minute = ('0' + str(minute))[-2:] + yield hour + ':' + minute + + return list(gen()) + + def gen(self, n, head, lst, func): + if head == len(lst): + yield None + + if n == 0: + yield 0 + + for i in xrange(head, len(lst)): + for rest in self.gen(n-1, i+1, lst, func): + if rest is not None: + ret = lst[i]+rest + if func(ret): + yield ret + else: + break + + def hour(self, n): + return self.gen(n, 0, self.hours, lambda x: x < 12) + + def minute(self, n): + return self.gen(n, 0, self.minutes, lambda x: x < 60) + + +if __name__ == "__main__": + assert Solution().readBinaryWatch(1) == ['0:01', '0:02', '0:04', '0:08', '0:16', '0:32', '1:00', '2:00', '4:00', + '8:00'] From 302931e3d8947a8e34cd2712572244e94a410d59 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 27 Sep 2016 22:54:48 -0700 Subject: [PATCH 202/585] math --- 400 Nth Digit.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 400 Nth Digit.py diff --git a/400 Nth Digit.py b/400 Nth Digit.py new file mode 100644 index 0000000..e4aa088 --- /dev/null +++ b/400 Nth Digit.py @@ -0,0 +1,45 @@ +""" +Find the nth digit of the infinite integer sequence 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... + +Note: +n is positive and will fit within the range of a 32-bit signed integer (n < 231). + +Example 1: + +Input: +3 + +Output: +3 +Example 2: + +Input: +11 + +Output: +0 + +Explanation: +The 11th digit of the sequence 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... is a 0, which is part of the number 10. +""" +__author__ = 'Daniel' + + +class Solution(object): + def findNthDigit(self, n): + """ + Math, quotient and remainder + :type n: int + :rtype: int + """ + digit_cnt = 1 + num_cnt = 9 + while n > digit_cnt * num_cnt: + n -= digit_cnt * num_cnt + digit_cnt += 1 + num_cnt *= 10 + + n -= 1 # debugging: without -1, it just pass over the target digit + q, r = n / digit_cnt, n % digit_cnt + target = num_cnt / 9 + q + return int(str(target)[r]) From b4effef20a884636afe6a4020273d104eaf83c06 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 28 Sep 2016 10:42:20 -0700 Subject: [PATCH 203/585] hexadecimal --- 405 Convert a Number to Hexadecimal.py | 65 ++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 405 Convert a Number to Hexadecimal.py diff --git a/405 Convert a Number to Hexadecimal.py b/405 Convert a Number to Hexadecimal.py new file mode 100644 index 0000000..eb8f2ea --- /dev/null +++ b/405 Convert a Number to Hexadecimal.py @@ -0,0 +1,65 @@ +""" +Given an integer, write an algorithm to convert it to hexadecimal. For negative integer, two's complement method is +used. + +Note: + +All letters in hexadecimal (a-f) must be in lowercase. +The hexadecimal string must not contain extra leading 0s. If the number is zero, it is represented by a single zero +character '0'; otherwise, the first character in the hexadecimal string will not be the zero character. +The given number is guaranteed to fit within the range of a 32-bit signed integer. +You must not use any method provided by the library which converts/formats the number to hex directly. +Example 1: + +Input: +26 + +Output: +"1a" +Example 2: + +Input: +-1 + +Output: +"ffffffff" +""" +__author__ = 'Daniel' + + +class Solution(object): + def toHex(self, num): + """ + All use bit manipulation + :type num: int + :rtype: str + """ + ret = [] + while len(ret) < 8 and num: + ret.append(self.encode(num & 0xf)) + num >>= 4 + + return ''.join(ret[::-1]) or '0' + + def toHexNormal(self, num): + """ + Python arithmetic handles the negative number very well + :type num: int + :rtype: str + """ + ret = [] + while len(ret) < 8 and num: + ret.append(self.encode(num % 16)) + num /= 16 + + return ''.join(ret[::-1]) or '0' + + def encode(self, d): + if 0 <= d < 10: + return str(d) + + return chr(ord('a') + d - 10) + + +if __name__ == "__main__": + assert Solution().toHex(-1) == 'ffffffff' From e03f4a7124dbca2f1d63c82a98dec726891d7b6a Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 28 Sep 2016 13:11:33 -0700 Subject: [PATCH 204/585] bit manipulation --- 397 Integer Replacement.py | 72 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 397 Integer Replacement.py diff --git a/397 Integer Replacement.py b/397 Integer Replacement.py new file mode 100644 index 0000000..2a0332c --- /dev/null +++ b/397 Integer Replacement.py @@ -0,0 +1,72 @@ +""" +Given a positive integer n and you can do operations as follow: + +If n is even, replace n with n/2. +If n is odd, you can replace n with either n + 1 or n - 1. +What is the minimum number of replacements needed for n to become 1? + +Example 1: + +Input: +8 + +Output: +3 + +Explanation: +8 -> 4 -> 2 -> 1 +Example 2: + +Input: +7 + +Output: +4 + +Explanation: +7 -> 8 -> 4 -> 2 -> 1 +or +7 -> 6 -> 3 -> 2 -> 1 +""" +__author__ = 'Daniel' + + +class Solution(object): + def integerReplacement(self, n): + """ + Simulation using dp fails since bi-directional + Simple recursion + + Math solution: bit manipulation + https://discuss.leetcode.com/topic/58334/a-couple-of-java-solutions-with-explanations/ + 3 is a special case + :type n: int + :rtype: int + """ + ret = 0 + while n != 1: + ret += 1 + if n & 1 == 0: + n >>= 1 + elif n == 0b11 or n >> 1 & 1 == 0: + n -= 1 + else: + n += 1 + + return ret + + def integerReplacementRecur(self, n): + """ + Simple recursion + :type n: int + :rtype: int + """ + if n == 1: return 0 + + ret = 1 + if n%2 == 0: + ret += self.integerReplacement(n/2) + else: + ret += min(self.integerReplacement(n+1), self.integerReplacement(n-1)) + + return ret From 97955e156f515847e4997e8af04e1e8e91fe8855 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 28 Sep 2016 13:11:56 -0700 Subject: [PATCH 205/585] math --- 396 Rotate Function.py | 52 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 396 Rotate Function.py diff --git a/396 Rotate Function.py b/396 Rotate Function.py new file mode 100644 index 0000000..397a9a4 --- /dev/null +++ b/396 Rotate Function.py @@ -0,0 +1,52 @@ +""" +Given an array of integers A and let n to be its length. + +Assume Bk to be an array obtained by rotating the array A k positions clock-wise, we define a "rotation function" F on +A as follow: + +F(k) = 0 * Bk[0] + 1 * Bk[1] + ... + (n-1) * Bk[n-1]. + +Calculate the maximum value of F(0), F(1), ..., F(n-1). + +Note: +n is guaranteed to be less than 105. + +Example: + +A = [4, 3, 2, 6] + +F(0) = (0 * 4) + (1 * 3) + (2 * 2) + (3 * 6) = 0 + 3 + 4 + 18 = 25 +F(1) = (0 * 6) + (1 * 4) + (2 * 3) + (3 * 2) = 0 + 4 + 6 + 6 = 16 +F(2) = (0 * 2) + (1 * 6) + (2 * 4) + (3 * 3) = 0 + 6 + 8 + 9 = 23 +F(3) = (0 * 3) + (1 * 2) + (2 * 6) + (3 * 4) = 0 + 2 + 12 + 12 = 26 + +So the maximum value of F(0), F(1), F(2), F(3) is F(3) = 26. +""" +import sys + +__author__ = 'Daniel' + + +class Solution(object): + def maxRotateFunction(self, A): + """ + See the rotation pattern + :type A: List[int] + :rtype: int + """ + if not A: return 0 + + gmax = -sys.maxint + n = len(A) + s = sum(A) + + cur = sum(idx * val for idx, val in enumerate(A)) + for r in reversed(A): + cur = cur + s - n * r + gmax = max(gmax, cur) + + return gmax + + +if __name__ == "__main__": + assert Solution().maxRotateFunction([4, 3, 2, 6]) == 26 From 64e8bfe7ce4f8f31eab2702b34c77def13ae3f63 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 29 Sep 2016 22:28:35 -0700 Subject: [PATCH 206/585] dp --- 403 Frog Jump.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 403 Frog Jump.py diff --git a/403 Frog Jump.py b/403 Frog Jump.py new file mode 100644 index 0000000..80c1427 --- /dev/null +++ b/403 Frog Jump.py @@ -0,0 +1,67 @@ +""" +A frog is crossing a river. The river is divided into x units and at each unit there may or may not exist a stone. The +frog can jump on a stone, but it must not jump into the water. + +Given a list of stones' positions (in units) in sorted ascending order, determine if the frog is able to cross the river +by landing on the last stone. Initially, the frog is on the first stone and assume the first jump must be 1 unit. + +If the frog's last jump was k units, then its next jump must be either k - 1, k, or k + 1 units. Note that the frog can +only jump in the forward direction. + +Note: + +The number of stones is >= 2 and is < 1,100. +Each stone's position will be a non-negative integer < 231. +The first stone's position is always 0. +Example 1: + +[0,1,3,5,6,8,12,17] + +There are a total of 8 stones. +The first stone at the 0th unit, second stone at the 1st unit, +third stone at the 3rd unit, and so on... +The last stone at the 17th unit. + +Return true. The frog can jump to the last stone by jumping +1 unit to the 2nd stone, then 2 units to the 3rd stone, then +2 units to the 4th stone, then 3 units to the 6th stone, +4 units to the 7th stone, and 5 units to the 8th stone. +Example 2: + +[0,1,2,3,4,8,9,11] + +Return false. There is no way to jump to the last stone as +the gap between the 5th and 6th stone is too large. +""" +__author__ = 'Daniel' + + +class Solution(object): + def canCross(self, stones): + """ + F, step table + Let F[i] be stone at position i, + + dp with a set as the table cell. + :type stones: List[int] + :rtype: bool + """ + F = {} + for stone in stones: + F[stone] = set() + + F[0].add(0) + for stone in stones: + for step in F[stone]: + for i in (-1, 0, 1): + nxt = stone+step+i + if nxt != stone and nxt in F: + F[nxt].add(step+i) + + return True if F[stones[-1]] else False + + +if __name__ == "__main__": + assert Solution().canCross([0, 2]) == False + assert Solution().canCross([0, 1, 3, 5, 6, 8, 12, 17]) == True + assert Solution().canCross([0, 1, 2, 3, 4, 8, 9, 11]) == False From 370e5cd32c687c4e789f4daa77625c211ff35fd3 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 29 Sep 2016 22:31:12 -0700 Subject: [PATCH 207/585] backtracking --- 301 Remove Invalid Parentheses.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/301 Remove Invalid Parentheses.py b/301 Remove Invalid Parentheses.py index 8db361a..3614acf 100644 --- a/301 Remove Invalid Parentheses.py +++ b/301 Remove Invalid Parentheses.py @@ -15,8 +15,11 @@ class Solution(object): def removeInvalidParentheses(self, s): """ + Brute force: BFS and then validate + Algorithm focuses on left parentheses - Backtracking/DFS with pruning + Backtracking/DFS with prune & jump + :type s: str :rtype: List[str] """ @@ -27,6 +30,7 @@ def removeInvalidParentheses(self, s): def minrm(self, s): """ + Find the minimal removal count to limit the search depth returns minimal number of removals """ rmcnt = 0 @@ -76,7 +80,3 @@ def dfs(self, s, cur, left, pi, i, rmcnt, ret): if __name__ == "__main__": assert Solution().removeInvalidParentheses("(a)())()") == ['(a())()', '(a)()()'] - - - - From 4e41eda21ab69f228463e6872d95332514be7acf Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 29 Sep 2016 22:32:14 -0700 Subject: [PATCH 208/585] math --- 273 Integer to English Words.py | 38 ++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/273 Integer to English Words.py b/273 Integer to English Words.py index ea1f66c..28a8ad4 100644 --- a/273 Integer to English Words.py +++ b/273 Integer to English Words.py @@ -11,7 +11,7 @@ class Solution(object): def __init__(self): - self.m = { + self.table = { 0: None, 1: "One", 2: "Two", @@ -52,37 +52,41 @@ def numberToWords(self, num): :type num: int :rtype: str """ - if num == 0: - return "Zero" + if num == 0: return "Zero" ret = [] self.toWords(num, ret) - ret = filter(lambda x: x, ret) # filter zeros + ret = filter(lambda x: x, ret) # filter None as zeros return " ".join(map(str, ret)) def toWords(self, num, ret): + """ + will call partial_parse + + significance by significance + """ SIGS = [1000000000, 1000000, 1000, 100] for SIG in SIGS: - num = self.partial_parse(num, SIG, ret) + self.partial_parse(num, SIG, ret) + num %= SIG TEN = 10 if num/TEN > 1: - ret.append(self.m[(num/TEN)*TEN]) - ret.append(self.m[num%TEN]) - else: - ret.append(self.m[num]) + ret.append(self.table[(num/TEN)*TEN]) + + ret.append(self.table[num%TEN]) def partial_parse(self, num, sig, ret): + """ + will call toWords + """ if num/sig: - pre = [] - self.toWords(num/sig, pre) - ret.extend(pre) - ret.append(self.m[sig]) - num %= sig - - return num + prefix = [] + self.toWords(num/sig, prefix) + ret.extend(prefix) + ret.append(self.table[sig]) if __name__ == "__main__": assert Solution().numberToWords(1234567891) == "One Billion Two Hundred Thirty Four Million Five Hundred Sixty " \ - "Seven Thousand Eight Hundred Ninety One" \ No newline at end of file + "Seven Thousand Eight Hundred Ninety One" From 9e15ab8fa0f59972a3cf5c741b8a481df37a7973 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 29 Sep 2016 22:32:49 -0700 Subject: [PATCH 209/585] two pointers --- 283 Move Zeroes.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/283 Move Zeroes.py b/283 Move Zeroes.py index fe948ac..5e932c1 100644 --- a/283 Move Zeroes.py +++ b/283 Move Zeroes.py @@ -15,6 +15,7 @@ class Solution(object): def moveZeroes(self, nums): """ + Two pointers at the left side Pivot """ left = -1 @@ -31,17 +32,17 @@ def moveZeroes(self, nums): :type nums: List[int] :rtype: void Do not return anything, modify nums in-place instead. """ - i = 0 + cnt = 0 for elt in nums: if elt != 0: - nums[i] = elt - i += 1 + nums[cnt] = elt + cnt += 1 - for j in xrange(i, len(nums)): + for j in xrange(cnt, len(nums)): nums[j] = 0 if __name__ == "__main__": lst = [0, 1, 0, 3, 12] Solution().moveZeroes(lst) - assert lst == [1, 3, 12, 0, 0] \ No newline at end of file + assert lst == [1, 3, 12, 0, 0] From 3b0a79a7ffb618362efcbc8b1053c7cee051f88b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 29 Sep 2016 23:00:18 -0700 Subject: [PATCH 210/585] RandomizedSet --- 380 Insert Delete GetRandom O(1).py | 130 ++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 380 Insert Delete GetRandom O(1).py diff --git a/380 Insert Delete GetRandom O(1).py b/380 Insert Delete GetRandom O(1).py new file mode 100644 index 0000000..2b0763a --- /dev/null +++ b/380 Insert Delete GetRandom O(1).py @@ -0,0 +1,130 @@ +""" +Design a data structure that supports all following operations in average O(1) time. + +insert(val): Inserts an item val to the set if not already present. +remove(val): Removes an item val from the set if present. +getRandom: Returns a random element from current set of elements. Each element must have the same probability of +being returned. +Example: + +// Init an empty set. +RandomizedSet randomSet = new RandomizedSet(); + +// Inserts 1 to the set. Returns true as 1 was inserted successfully. +randomSet.insert(1); + +// Returns false as 2 does not exist in the set. +randomSet.remove(2); + +// Inserts 2 to the set, returns true. Set now contains [1,2]. +randomSet.insert(2); + +// getRandom should return either 1 or 2 randomly. +randomSet.getRandom(); + +// Removes 1 from the set, returns true. Set now contains [2]. +randomSet.remove(1); + +// 2 was already in the set, so return false. +randomSet.insert(2); + +// Since 1 is the only number in the set, getRandom always return 1. +randomSet.getRandom(); +""" +import random + +__author__ = 'Daniel' + + +class RandomizedSet(object): + def __init__(self): + """ + 1. Use List of numbers to support O(1) getRandom + 2. need an efficient way to find and delete an element + 3. Use Map to get the location, move to end and pop + Initialize your data structure here. + """ + self.lst = [] + self.pos = {} + + def insert(self, val): + """ + Inserts a value to the set. Returns true if the set did not already contain the specified element. + :type val: int + :rtype: bool + """ + if val in self.pos: + return False + + self.lst.append(val) + self.pos[val] = len(self.lst) - 1 + + return True + + def remove(self, val): + """ + Removes a value from the set. Returns true if the set contained the specified element. + :type val: int + :rtype: bool + """ + if val not in self.pos: + return False + + idx, last = self.pos[val], len(self.lst) - 1 + self.lst[idx], self.lst[last] = self.lst[last], self.lst[idx] + self.pos[self.lst[idx]] = idx + + del self.pos[val] + self.lst.pop() + + return True + + def getRandom(self): + """ + Gets a random element from the set. + :rtype: int + """ + return random.choice(self.lst) + + +class RandomizedSetTLE(object): + def __init__(self): + """ + Initialize your data structure here. + """ + self.set = set() + + def insert(self, val): + """ + Inserts a value to the set. Returns true if the set did not already contain the specified element. + :type val: int + :rtype: bool + """ + ret = val not in self.set + self.set.add(val) + return ret + + def remove(self, val): + """ + Removes a value from the set. Returns true if the set contained the specified element. + :type val: int + :rtype: bool + """ + ret = val in self.set + self.set.discard(val) + return ret + + def getRandom(self): + """ + Get a random element from the set. + :rtype: int + """ + return random.sample(self.set, 1)[0] # O(N), equivalent to random.choice(tuple(allLetters)) + + + +# Your RandomizedSet object will be instantiated and called as such: +# obj = RandomizedSet() +# param_1 = obj.insert(val) +# param_2 = obj.remove(val) +# param_3 = obj.getRandom() From 6c5c632e5eecb34c8b2f6b20c2f5d8835fb242e7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 30 Sep 2016 00:15:52 -0700 Subject: [PATCH 211/585] random set with duplication --- 380 Insert Delete GetRandom O(1).py | 6 +- ...ete GetRandom O(1) - Duplicates allowed.py | 91 +++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 381 Insert Delete GetRandom O(1) - Duplicates allowed.py diff --git a/380 Insert Delete GetRandom O(1).py b/380 Insert Delete GetRandom O(1).py index 2b0763a..d70cdd4 100644 --- a/380 Insert Delete GetRandom O(1).py +++ b/380 Insert Delete GetRandom O(1).py @@ -71,8 +71,10 @@ def remove(self, val): return False idx, last = self.pos[val], len(self.lst) - 1 - self.lst[idx], self.lst[last] = self.lst[last], self.lst[idx] - self.pos[self.lst[idx]] = idx + + if idx != last: + self.lst[idx], self.lst[last] = self.lst[last], self.lst[idx] + self.pos[self.lst[idx]] = idx del self.pos[val] self.lst.pop() diff --git a/381 Insert Delete GetRandom O(1) - Duplicates allowed.py b/381 Insert Delete GetRandom O(1) - Duplicates allowed.py new file mode 100644 index 0000000..7f91800 --- /dev/null +++ b/381 Insert Delete GetRandom O(1) - Duplicates allowed.py @@ -0,0 +1,91 @@ +""" +Design a data structure that supports all following operations in average O(1) time. + +Note: Duplicate elements are allowed. +insert(val): Inserts an item val to the collection. +remove(val): Removes an item val from the collection if present. +getRandom: Returns a random element from current collection of elements. The probability of each element being returned +is linearly related to the number of same value the collection contains. +Example: + +// Init an empty collection. +RandomizedCollection collection = new RandomizedCollection(); + +// Inserts 1 to the collection. Returns true as the collection did not contain 1. +collection.insert(1); + +// Inserts another 1 to the collection. Returns false as the collection contained 1. Collection now contains [1,1]. +collection.insert(1); + +// Inserts 2 to the collection, returns true. Collection now contains [1,1,2]. +collection.insert(2); + +// getRandom should return 1 with the probability 2/3, and returns 2 with the probability 1/3. +collection.getRandom(); + +// Removes 1 from the collection, returns true. Collection now contains [1,2]. +collection.remove(1); + +// getRandom should return 1 and 2 both equally likely. +collection.getRandom(); +""" +from collections import defaultdict +import random + +__author__ = 'Daniel' + + +class RandomizedCollection(object): + def __init__(self): + """ + pop set is O(1), deterministic depends on hash value + Initialize your data structure here. + """ + self.lst = [] + self.pos = defaultdict(set) + + def insert(self, val): + """ + Inserts a value to the collection. Returns true if the collection did not already contain the specified element. + :type val: int + :rtype: bool + """ + flag = True if not self.pos[val] else False + + self.lst.append(val) + self.pos[val].add(len(self.lst) - 1) + + return flag + + def remove(self, val): + """ + Removes a value from the collection. Returns true if the collection contained the specified element. + :type val: int + :rtype: bool + """ + if not self.pos[val]: + return False + + idx, last = self.pos[val].pop(), len(self.lst) - 1 + if idx != last: + self.lst[idx], self.lst[last] = self.lst[last], self.lst[idx] + self.pos[self.lst[idx]].remove(last) + self.pos[self.lst[idx]].add(idx) + + self.lst.pop() + + return True + + def getRandom(self): + """ + Get a random element from the collection. + :rtype: int + """ + return random.choice(self.lst) + + +# Your RandomizedCollection object will be instantiated and called as such: +# obj = RandomizedCollection() +# param_1 = obj.insert(val) +# param_2 = obj.remove(val) +# param_3 = obj.getRandom() \ No newline at end of file From 91653d89e8a70fbb80c6be5b9ebb660ae11945e0 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 30 Sep 2016 11:14:04 -0700 Subject: [PATCH 212/585] dfs --- 329 Longest Increasing Path in a Matrix.py | 74 ++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 329 Longest Increasing Path in a Matrix.py diff --git a/329 Longest Increasing Path in a Matrix.py b/329 Longest Increasing Path in a Matrix.py new file mode 100644 index 0000000..f0497f6 --- /dev/null +++ b/329 Longest Increasing Path in a Matrix.py @@ -0,0 +1,74 @@ +""" +Given an integer matrix, find the length of the longest increasing path. + +From each cell, you can either move to four directions: left, right, up or down. You may NOT move diagonally or move +outside of the boundary (i.e. wrap-around is not allowed). + +Example 1: + +nums = [ + [9,9,4], + [6,6,8], + [2,1,1] +] +Return 4 +The longest increasing path is [1, 2, 6, 9]. + +Example 2: + +nums = [ + [3,4,5], + [3,2,6], + [2,2,1] +] +Return 4 +The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed. +""" +__author__ = 'Daniel' + + +class Solution(object): + def __init__(self): + self.cache = None + self.dirs = ((-1, 0), (1, 0), (0, -1), (0, 1),) + + def longestIncreasingPath(self, matrix): + """ + dfs + cache + :type matrix: List[List[int]] + :rtype: int + """ + if not matrix: return 0 + + m, n = len(matrix), len(matrix[0]) + self.cache = [[None for _ in xrange(n)] for _ in xrange(m)] + gmax = 1 + for i in xrange(m): + for j in xrange(n): + gmax = max(gmax, self.longest(matrix, i, j)) + + return gmax + + def longest(self, matrix, i, j): + """ + Strictly increasing, thus no need to have a visited matrix + """ + if not self.cache[i][j]: + m, n = len(matrix), len(matrix[0]) + maxa = 1 + for d in self.dirs: + I, J = i + d[0], j + d[1] + if 0 <= I < m and 0 <= J < n and matrix[I][J] > matrix[i][j]: + maxa = max(maxa, 1 + self.longest(matrix, I, J)) + + self.cache[i][j] = maxa + + return self.cache[i][j] + + +if __name__ == "__main__": + assert Solution().longestIncreasingPath([ + [9, 9, 4], + [6, 6, 8], + [2, 1, 1] + ]) == 4 From 7dcd4463fa7f4d0db7acd9965b5d5f9989d2e0ad Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 30 Sep 2016 12:13:23 -0700 Subject: [PATCH 213/585] sliding window --- ...ring with At Most K Distinct Characters.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 340 Longest Substring with At Most K Distinct Characters.py diff --git a/340 Longest Substring with At Most K Distinct Characters.py b/340 Longest Substring with At Most K Distinct Characters.py new file mode 100644 index 0000000..1770200 --- /dev/null +++ b/340 Longest Substring with At Most K Distinct Characters.py @@ -0,0 +1,47 @@ +""" +Given a string, find the length of the longest substring T that contains at most k distinct characters. + +For example, Given s = "eceba" and k = 2, + +T is "ece" which its length is 3. + +Show Company Tags +Show Tags +Show Similar Problems +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class Solution(object): + def lengthOfLongestSubstringKDistinct(self, s, k): + """ + Brute force: O(n^2 * n) + + Sliding window O(n) + :type s: str + :type k: int + :rtype: int + """ + st = 0 # start + counter = defaultdict(int) + maxa = 0 + for idx, val in enumerate(s): + if counter[val] == 0: + k -= 1 + + counter[val] += 1 + while k < 0: + counter[s[st]] -= 1 + if counter[s[st]] == 0: + k += 1 + st += 1 + + maxa = max(maxa, idx - st + 1) + + return maxa + + +if __name__ == "__main__": + assert Solution().lengthOfLongestSubstringKDistinct("eceba", 2) == 3 From d408a93760a2f9144e87002f4f24b51f6dc55504 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 1 Oct 2016 12:17:51 -0700 Subject: [PATCH 214/585] greedy --- 330 Patching Array.py | 93 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 330 Patching Array.py diff --git a/330 Patching Array.py b/330 Patching Array.py new file mode 100644 index 0000000..271813b --- /dev/null +++ b/330 Patching Array.py @@ -0,0 +1,93 @@ +""" +Given a sorted positive integer array nums and an integer n, add/patch elements to the array such that any number in +range [1, n] inclusive can be formed by the sum of some elements in the array. Return the minimum number of patches +required. + +Example 1: +nums = [1, 3], n = 6 +Return 1. + +Combinations of nums are [1], [3], [1,3], which form possible sums of: 1, 3, 4. +Now if we add/patch 2 to nums, the combinations are: [1], [2], [3], [1,3], [2,3], [1,2,3]. +Possible sums are 1, 2, 3, 4, 5, 6, which now covers the range [1, 6]. +So we only need 1 patch. + +Example 2: +nums = [1, 5, 10], n = 20 +Return 2. +The two patches can be [2, 4]. + +Example 3: +nums = [1, 2, 2], n = 5 +Return 0. +""" +__author__ = 'Daniel' + + +class Solution(object): + def minPatches(self, nums, n): + """ + https://discuss.leetcode.com/topic/35494/solution-explanation + + Greedy + Let cur_max be the current max sum can be formed by [0, i) when iterating at i-th index + if cur_max < Ai: + we have a void gap at [cur_max + 1, Ai] + we need to patch a cur_max in the array to maximize the all-cover reach + else: + cur_max += Ai + + :type nums: List[int] + :type n: int + :rtype: int + """ + cnt = 0 + cur_max = 0 + i = 0 + while cur_max < n: + if i >= len(nums) or cur_max + 1 < nums[i]: + cur_max += cur_max + 1 + cnt += 1 + else: + cur_max += nums[i] + i += 1 + + return cnt + + def minPatches2(self, nums, n): + """ + https://discuss.leetcode.com/topic/35494/solution-explanation + + Greedy + Let cur_max be the current max sum can be formed by [0, i) when iterating at i-th index + if cur_max < Ai: + we have a void gap at [cur_max + 1, Ai] + we need to patch a cur_max in the array to maximize the all-cover reach + else: + cur_max += Ai + + :type nums: List[int] + :type n: int + :rtype: int + """ + nums = filter(lambda x: x <= n, nums) + + cnt = 0 + cur_max = 0 + for elt in nums: + while cur_max + 1 < elt: + cur_max += cur_max + 1 + cnt += 1 + + cur_max += elt + + # after iterating all array element + while cur_max < n: + cur_max += cur_max + 1 + cnt += 1 + + return cnt + + +if __name__ == "__main__": + assert Solution().minPatches([1, 2, 2, 6, 34], 20) == 1 \ No newline at end of file From 055b1f1983dffb2cb410b1681a43146255715450 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 1 Oct 2016 12:18:06 -0700 Subject: [PATCH 215/585] greedy + heap --- 358 Rearrange String k Distance Apart.py | 87 ++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 358 Rearrange String k Distance Apart.py diff --git a/358 Rearrange String k Distance Apart.py b/358 Rearrange String k Distance Apart.py new file mode 100644 index 0000000..cd3a7fe --- /dev/null +++ b/358 Rearrange String k Distance Apart.py @@ -0,0 +1,87 @@ +""" +Given a non-empty string str and an integer k, rearrange the string such that the same characters are at least distance +k from each other. + +All input strings are given in lowercase letters. If it is not possible to rearrange the string, return an empty string +"". + +Example 1: +str = "aabbcc", k = 3 + +Result: "abcabc" + +The same letters are at least distance 3 from each other. +Example 2: +str = "aaabc", k = 3 + +Answer: "" + +It is not possible to rearrange the string. +Example 3: +str = "aaadbbcc", k = 2 + +Answer: "abacabcd" + +Another possible answer is: "abcabcda" + +The same letters are at least distance 2 from each other. + +""" +from collections import defaultdict +import heapq + +__author__ = 'Daniel' + + +class Val(object): + def __init__(self, cnt, val): + self.cnt = cnt + self.val = val + + def __cmp__(self, other): + if self.cnt == other.cnt: + return cmp(self.val, other.val) + + return -cmp(self.cnt, other.cnt) + + +class Solution(object): + def rearrangeString(self, s, k): + """ + Greedy, largest first, fill k first + O(lg(26) n) + :type s: str + :type k: int + :rtype: str + """ + if not s or k == 0: return s + + d = defaultdict(int) + for c in s: + d[c] += 1 + + h = [] + for char, cnt in d.items(): + heapq.heappush(h, Val(cnt, char)) + + ret = [] + while len(ret) < len(s): + cur = [] + diff = len(s) - len(ret) + for _ in xrange(min(k, diff)): + if not h: return "" + + e = heapq.heappop(h) + ret.append(e.val) + e.cnt -= 1 + if e.cnt > 0: + cur.append(e) + + for e in cur: + heapq.heappush(h, e) + + return "".join(ret) + + +if __name__ == "__main__": + assert Solution().rearrangeString("aabbccdd", 4) == "abcdabcd" From 46e4555a92d9dabd8f29057de31a491ab1413f00 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 2 Oct 2016 10:10:46 -0700 Subject: [PATCH 216/585] easy --- 409 Longest Palindrome.py | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 409 Longest Palindrome.py diff --git a/409 Longest Palindrome.py b/409 Longest Palindrome.py new file mode 100644 index 0000000..5d43f96 --- /dev/null +++ b/409 Longest Palindrome.py @@ -0,0 +1,47 @@ +""" +Given a string which consists of lowercase or uppercase letters, find the length of the longest palindromes that can be +built with those letters. + +This is case sensitive, for example "Aa" is not considered a palindrome here. + +Note: +Assume the length of given string will not exceed 1,010. + +Example: + +Input: +"abccccdd" + +Output: +7 + +Explanation: +One longest palindrome that can be built is "dccaccd", whose length is 7. +""" +from collections import defaultdict + +__author__ = 'Daniel' + + +class Solution(object): + def longestPalindrome(self, s): + """ + :type s: str + :rtype: int + """ + c = defaultdict(int) + for elt in s: + c[elt] += 1 + + ret = 0 + for v in c.values(): + ret += (v/2) * 2 + + if any(map(lambda x: x % 2 == 1, c.values())): + ret += 1 + + return ret + + +if __name__ == "__main__": + assert Solution().longestPalindrome("abccccdd") == 7 From 85658221b9452a7ef0697df5d68a006ec0b8bd10 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 2 Oct 2016 10:22:11 -0700 Subject: [PATCH 217/585] pointers --- 408 Valid Word Abbreviation.py | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 408 Valid Word Abbreviation.py diff --git a/408 Valid Word Abbreviation.py b/408 Valid Word Abbreviation.py new file mode 100644 index 0000000..bf83fce --- /dev/null +++ b/408 Valid Word Abbreviation.py @@ -0,0 +1,36 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution(object): + def validWordAbbreviation(self, word, abbr): + """ + pointers + :type word: str + :type abbr: str + :rtype: bool + """ + w = 0 + a = 0 + while w < len(word) and a < len(abbr): + if abbr[a].isdigit() and abbr[a] != '0': + e = a + while e < len(abbr) and abbr[e].isdigit(): e += 1 + num = int(abbr[a:e]) + a = e + w += num + else: + if word[w] != abbr[a]: + return False + + w += 1 + a += 1 + + return w == len(word) and a == len(abbr) + + +if __name__ == "__main__": + assert Solution().validWordAbbreviation("internationalization", "i12iz4n") == True + assert Solution().validWordAbbreviation("apple", "a2e") == False From ecfdfe0ecc7e056759331540e2118ec3db21096b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 2 Oct 2016 14:51:14 -0700 Subject: [PATCH 218/585] backtracking --- 411 Minimum Unique Word Abbreviation.py | 73 +++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 411 Minimum Unique Word Abbreviation.py diff --git a/411 Minimum Unique Word Abbreviation.py b/411 Minimum Unique Word Abbreviation.py new file mode 100644 index 0000000..62472eb --- /dev/null +++ b/411 Minimum Unique Word Abbreviation.py @@ -0,0 +1,73 @@ +""" +Premium Question +""" +__author__ = 'Daniel' + + +class Solution(object): + def minAbbreviation(self, target, dictionary): + """ + :type target: str + :type dictionary: List[str] + :rtype: str + """ + ret = target + for abbr in self.dfs(target): + if self.validate(dictionary, abbr) and len(ret) > len(abbr): + ret = abbr + + return ret + + def dfs(self, word): + """ + backtracking, pivoting letter + :type word: str + :rtype: List[str] + """ + if not word: + return [""] + + ret = [] + for l in xrange(len(word)+1): + left_num = str(l) if l else "" + for right in self.dfs(word[l+1:]): + cur = left_num+word[l:l+1]+right + ret.append(cur) + + return ret + + def validate(self, dictionary, abbr): + for w in dictionary: + if self.validWordAbbreviation(w, abbr): + return False + + return True + + def validWordAbbreviation(self, word, abbr): + """ + pointers + :type word: str + :type abbr: str + :rtype: bool + """ + w = 0 + a = 0 + while w < len(word) and a < len(abbr): + if abbr[a].isdigit() and abbr[a] != '0': + e = a + while e < len(abbr) and abbr[e].isdigit(): e += 1 + num = int(abbr[a:e]) + a = e + w += num + else: + if word[w] != abbr[a]: + return False + + w += 1 + a += 1 + + return w == len(word) and a == len(abbr) + + +if __name__ == "__main__": + print Solution().minAbbreviation("apple", ["blade"]) From b126f474ad4d48d55068f75e8d487d06f998618b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 2 Oct 2016 15:42:19 -0700 Subject: [PATCH 219/585] backtracking --- 411 Minimum Unique Word Abbreviation.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/411 Minimum Unique Word Abbreviation.py b/411 Minimum Unique Word Abbreviation.py index 62472eb..650a00b 100644 --- a/411 Minimum Unique Word Abbreviation.py +++ b/411 Minimum Unique Word Abbreviation.py @@ -11,12 +11,12 @@ def minAbbreviation(self, target, dictionary): :type dictionary: List[str] :rtype: str """ - ret = target - for abbr in self.dfs(target): - if self.validate(dictionary, abbr) and len(ret) > len(abbr): - ret = abbr + ret = (target, len(target)) + for abbr, abbr_l in self.dfs(target): + if self.validate(dictionary, abbr) and ret[1] > abbr_l: + ret = (abbr, abbr_l) - return ret + return ret[0] def dfs(self, word): """ @@ -25,14 +25,17 @@ def dfs(self, word): :rtype: List[str] """ if not word: - return [""] + return [("", 0)] ret = [] for l in xrange(len(word)+1): left_num = str(l) if l else "" - for right in self.dfs(word[l+1:]): - cur = left_num+word[l:l+1]+right - ret.append(cur) + left_l = 1 if left_num != "" else 0 + left_l += 1 if l < len(word) else 0 + + for right, right_l in self.dfs(word[l+1:]): + cur = left_num + word[l:l+1] + right # word[l:l+1] possible "" + ret.append((cur, left_l + right_l)) return ret From b3d27da0009e641ebcb3eb2cdb7380e7fc2f4266 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Mon, 3 Oct 2016 17:37:09 -0700 Subject: [PATCH 220/585] doc --- 332 Reconstruct Itinerary.py | 9 ++------- 357 Count Numbers with Unique Digits.py | 5 ++--- 358 Rearrange String k Distance Apart.py | 8 ++++---- 368 Largest Divisible Subset.py | 3 +-- 386 Lexicographical Numbers.py | 9 +++++---- 411 Minimum Unique Word Abbreviation.py | 2 +- 6 files changed, 15 insertions(+), 21 deletions(-) diff --git a/332 Reconstruct Itinerary.py b/332 Reconstruct Itinerary.py index 1bdf457..5e26c41 100644 --- a/332 Reconstruct Itinerary.py +++ b/332 Reconstruct Itinerary.py @@ -33,7 +33,7 @@ def findItinerary(self, tickets): G = defaultdict(list) for elt in tickets: s, e = elt - heapq.heappush(G[s], e) + heapq.heappush(G[s], e) # heap lexical order ret = deque() self.dfs(G, 'JFK', ret) @@ -42,14 +42,9 @@ def findItinerary(self, tickets): def dfs(self, G, cur, ret): while G[cur]: self.dfs(G, heapq.heappop(G[cur]), ret) + ret.appendleft(cur) if __name__ == "__main__": assert Solution().findItinerary([["JFK","KUL"],["JFK","NRT"],["NRT","JFK"]]) == ['JFK', 'NRT', 'JFK', 'KUL'] - - - - - - diff --git a/357 Count Numbers with Unique Digits.py b/357 Count Numbers with Unique Digits.py index e6078d4..d80476a 100644 --- a/357 Count Numbers with Unique Digits.py +++ b/357 Count Numbers with Unique Digits.py @@ -22,9 +22,8 @@ def countNumbersWithUniqueDigits(self, n): """ ret = 1 Fi = 1 - for i in xrange(1, n+1): - Fi *= (10-i+1) - if i == 1: Fi -= 1 + for i in xrange(n): + Fi *= (10-i) if i != 0 else 9 ret += Fi return ret diff --git a/358 Rearrange String k Distance Apart.py b/358 Rearrange String k Distance Apart.py index cd3a7fe..8df63c2 100644 --- a/358 Rearrange String k Distance Apart.py +++ b/358 Rearrange String k Distance Apart.py @@ -65,11 +65,11 @@ def rearrangeString(self, s, k): heapq.heappush(h, Val(cnt, char)) ret = [] - while len(ret) < len(s): + while h: cur = [] - diff = len(s) - len(ret) - for _ in xrange(min(k, diff)): - if not h: return "" + for _ in xrange(k): + if not h: + return "".join(ret) if len(ret) == len(s) else "" e = heapq.heappop(h) ret.append(e.val) diff --git a/368 Largest Divisible Subset.py b/368 Largest Divisible Subset.py index d8be3f2..6a8f0e4 100644 --- a/368 Largest Divisible Subset.py +++ b/368 Largest Divisible Subset.py @@ -27,7 +27,7 @@ def largestDivisibleSubset(self, A): divisible by the largest number in the divisible subset. Let F[i] for the size of subset ended with A[i] - F[i] = max(1 + F[j] if A[i] % A[j] for j in xrange(i-1)) + F[i] = max(1 + F[j] if A[i] % A[j] == 0 for j in xrange(i-1)) pi[i] = argmax(...) :type A: List[int] :rtype: List[int] @@ -63,4 +63,3 @@ def largestDivisibleSubset(self, A): if __name__ == "__main__": assert Solution().largestDivisibleSubset([1, 2, 4, 8]) == [1, 2, 4, 8] - diff --git a/386 Lexicographical Numbers.py b/386 Lexicographical Numbers.py index 7336e7d..f9474f7 100644 --- a/386 Lexicographical Numbers.py +++ b/386 Lexicographical Numbers.py @@ -16,15 +16,14 @@ def lexicalOrder(self, n): """ def gen(): i = 1 - for _ in xrange(n): + for _ in xrange(n): # erroneous for while i <= n: yield i if i * 10 <= n: i *= 10 # * 10 elif i % 10 != 9 and i + 1 <= n: i += 1 # for current digit else: - i /= 10 # move to next digit - while i % 10 == 9: + while i % 10 == 9 or i + 1 > n: i /= 10 i += 1 @@ -47,5 +46,7 @@ def lexicalOrderError(self, n): return ret + if __name__ == "__main__": - print Solution().lexicalOrder(100) \ No newline at end of file + assert Solution().lexicalOrder(30) == [1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 3, 30, 4, 5, 6, 7, 8, 9] diff --git a/411 Minimum Unique Word Abbreviation.py b/411 Minimum Unique Word Abbreviation.py index 650a00b..c21242c 100644 --- a/411 Minimum Unique Word Abbreviation.py +++ b/411 Minimum Unique Word Abbreviation.py @@ -73,4 +73,4 @@ def validWordAbbreviation(self, word, abbr): if __name__ == "__main__": - print Solution().minAbbreviation("apple", ["blade"]) + assert Solution().minAbbreviation("apple", ["blade"]) == "a4" From 83dae43dfe085d29cc7694679fea328f2bc2b725 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 4 Oct 2016 21:21:17 -0700 Subject: [PATCH 221/585] merge sort range sum --- 327 Count of Range Sum.py | 63 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 327 Count of Range Sum.py diff --git a/327 Count of Range Sum.py b/327 Count of Range Sum.py new file mode 100644 index 0000000..270f50a --- /dev/null +++ b/327 Count of Range Sum.py @@ -0,0 +1,63 @@ +""" +Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive. +Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i <= j), inclusive. + +Note: +A naive algorithm of O(n2) is trivial. You MUST do better than that. + +Example: +Given nums = [-2, 5, -1], lower = -2, upper = 2, +Return 3. +The three ranges are : [0, 0], [2, 2], [0, 2] and their respective sums are: -2, -1, 2. +""" +__author__ = 'Daniel' + + +class Solution(object): + def countRangeSum(self, nums, lower, upper): + """ + MergeSort while counting required range sum + :type nums: List[int] + :type lower: int + :type upper: int + :rtype: int + """ + if not nums: return 0 + + def msort(A, lo, hi): + if hi - lo <= 1: return 0 + + mid = (lo + hi)/2 + cnt = msort(A, lo, mid) + msort(A, mid, hi) + + temp = [] + i = j = r = mid + for l in xrange(lo, mid): + while i < hi and A[i] - A[l] < lower: i += 1 + while j < hi and A[j] - A[l] <= upper: j += 1 + cnt += j - i + + while r < hi and A[r] < A[l]: + temp.append(A[r]) + r += 1 + + temp.append(A[l]) + + while r < hi: # dangling right + temp.append(A[r]) + r += 1 + + A[lo:hi] = temp # A[lo:hi] = sorted(A[lo:hi] # Timsort, linear time + return cnt + + n = len(nums) + F = [0 for _ in xrange(n+1)] + for i in xrange(1, n+1): + F[i] = F[i-1] + nums[i-1] + + return msort(F, 0, n+1) + + +if __name__ == "__main__": + assert Solution().countRangeSum([0, 0], 0, 0) == 3 + assert Solution().countRangeSum([-2, 5, -1], -2, 2) == 3 From 7e37bac28045f712be41a4dfc0953317536b2ecd Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 4 Oct 2016 21:50:20 -0700 Subject: [PATCH 222/585] BFS with heap --- 407 Trapping Rain Water II.py | 101 ++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 407 Trapping Rain Water II.py diff --git a/407 Trapping Rain Water II.py b/407 Trapping Rain Water II.py new file mode 100644 index 0000000..9c30497 --- /dev/null +++ b/407 Trapping Rain Water II.py @@ -0,0 +1,101 @@ +""" +Given an m x n matrix of positive integers representing the height of each unit cell in a 2D elevation map, compute the +volume of water it is able to trap after raining. + +Note: +Both m and n are less than 110. The height of each unit cell is greater than 0 and is less than 20,000. + +Example: + +Given the following 3x6 height map: +[ + [1,4,3,1,3,2], + [3,2,1,3,2,4], + [2,3,3,2,3,1] +] + +Return 4. + +The above image represents the elevation map [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]] before the rain. + + +After the rain, water are trapped between the blocks. The total volume of water trapped is 4. +""" +import heapq + +__author__ = 'Daniel' + + +class Cell: + def __init__(self, i, j, h): + self.i = i + self.j = j + self.h = h + + def __cmp__(self, other): + return self.h - other.h + + +class Solution(object): + def __init__(self): + self.dirs = [(-1, 0), (1, 0), (0, -1), (0, 1)] + + def trapRainWater(self, mat): + """ + Find the min height with no water that higher than the current height and keep it. + Starting from the min height with no water + Do BFS with heap (similar to Dijkstra's algorithm) + + :param mat: List[List[int]] + :return: an integer + """ + if not mat: return 0 + + m, n = len(mat), len(mat[0]) + visited = [[False for _ in xrange(n)] for _ in xrange(m)] + h = [] + # add cells at the four edges + for i in xrange(m): + visited[i][0] = True + heapq.heappush(h, Cell(i, 0, mat[i][0])) + visited[i][n-1] = True + heapq.heappush(h, Cell(i, n-1, mat[i][n-1])) + + for j in xrange(1, n-1): + visited[0][j] = True + heapq.heappush(h, Cell(0, j, mat[0][j])) + visited[m-1][j] = True + heapq.heappush(h, Cell(m-1, j, mat[m-1][j])) + + # BFS with heap + trapped = 0 + while h: + cur = heapq.heappop(h) + for dir in self.dirs: + I, J = cur.i+dir[0], cur.j+dir[1] + if 0 <= I < m and 0 <= J < n and not visited[I][J]: + nxt = Cell(I, J, mat[I][J]) + if nxt.h < cur.h: # fill + trapped += cur.h - nxt.h + nxt.h = cur.h + + visited[I][J] = True + heapq.heappush(h, nxt) + + return trapped + + +if __name__ == "__main__": + assert Solution().trapRainWater([ + [12, 13, 0, 12], + [13, 4, 13, 12], + [13, 8, 10, 12], + [12, 13, 12, 12], + [13, 13, 13, 13]] + ) == 14 + assert Solution().trapRainWater([ + [9, 1, 10, 10], + [9, 1, 2, 8], + [2, 6, 5, 0], + [6, 0, 9, 0]] + ) == 0 From fce8438a4368cc84a4fb00a403ea99bfa4aa6dc4 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 5 Oct 2016 11:17:24 -0700 Subject: [PATCH 223/585] LIS --- 354 Russian Doll Envelopes.py | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 354 Russian Doll Envelopes.py diff --git a/354 Russian Doll Envelopes.py b/354 Russian Doll Envelopes.py new file mode 100644 index 0000000..66e3321 --- /dev/null +++ b/354 Russian Doll Envelopes.py @@ -0,0 +1,64 @@ +""" +You have a number of envelopes with widths and heights given as a pair of integers (w, h). One envelope can fit into +another if and only if both the width and height of one envelope is greater than the width and height of the other +envelope. + +What is the maximum number of envelopes can you Russian doll? (put one inside other) + +Example: +Given envelopes = [[5,4],[6,4],[6,7],[2,3]], the maximum number of envelopes you can Russian doll is 3 ([2,3] => [5,4] +=> [6,7]). +""" +import bisect + +__author__ = 'Daniel' + + +class Solution(object): + def maxEnvelopes(self, A): + """ + LIS + binary search + + sort by width first ascending, then sort by height descending (otherwise [3, 3] put in [3, 4]). + :type A: List[List[int]] + :rtype: int + """ + if not A: return 0 + + A.sort(key=lambda (w, h): (w, -h)) + F = [-1 for _ in xrange(len(A)+1)] + + F[1] = A[0][1] # store value rather than index + k = 1 + for _, h in A[1:]: + idx = bisect.bisect_left(F, h, 1, k+1) + F[idx] = h + k += 1 if idx == k+1 else 0 + + return k + + def maxEnvelopesTLE(self, A): + """ + LIS + O(n^2) + :type A: List[List[int]] + :rtype: int + """ + if not A: return 0 + + predicate = lambda a, b: b[0] > a[0] and b[1] > a[1] + A.sort() + n = len(A) + F = [1 for _ in xrange(n)] + for i in xrange(1, n): + for j in xrange(i): + if predicate(A[j], A[i]): + F[i] = max(F[i], 1 + F[j]) + + return max(F) + + +if __name__ == "__main__": + assert Solution().maxEnvelopes([[5, 4], [6, 4], [6, 7], [2, 3]]) == 3 + assert Solution().maxEnvelopes([[2,100],[3,200],[4,300],[5,500],[5,400],[5,250],[6,370],[6,360],[7,380]]) == 5 From 26c302ceb827056cb295b7b05e76b9a7c5bb7a15 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 5 Oct 2016 11:24:59 -0700 Subject: [PATCH 224/585] LIS --- 300 Longest Increasing Subsequence.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/300 Longest Increasing Subsequence.py b/300 Longest Increasing Subsequence.py index 8955953..009abde 100644 --- a/300 Longest Increasing Subsequence.py +++ b/300 Longest Increasing Subsequence.py @@ -10,6 +10,8 @@ Follow up: Could you improve it to O(n log n) time complexity? """ +import bisect + __author__ = 'Daniel' @@ -25,17 +27,14 @@ def lengthOfLIS(self, A): n = len(A) MIN = [-1 for _ in xrange(n+1)] - l = 1 - MIN[l] = 0 - for i in xrange(1,n): - if A[i] > A[MIN[l]]: - l += 1 - MIN[l] = i - else: - j = self.bin_search(MIN, A, A[i], 1, l+1) - MIN[j] = i + k = 1 + MIN[k] = A[0] # store value rather than index + for v in A[1:]: + idx = bisect.bisect_left(MIN, v, 1, k+1) + MIN[idx] = v + k += 1 if idx == k+1 else 0 - return l + return k def bin_search(self, M, A, t, lo=0, hi=None): if not hi: hi = len(M) @@ -121,4 +120,4 @@ def lengthOfLIS_dp(self, A): if __name__ == "__main__": - print Solution().lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18]) \ No newline at end of file + assert Solution().lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18]) == 4 \ No newline at end of file From 21c7dedaa671ecf08fd1308837c4f216dc282638 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 5 Oct 2016 13:10:50 -0700 Subject: [PATCH 225/585] merge intervals --- 352 Data Stream as Disjoint Intervals.py | 62 ++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 352 Data Stream as Disjoint Intervals.py diff --git a/352 Data Stream as Disjoint Intervals.py b/352 Data Stream as Disjoint Intervals.py new file mode 100644 index 0000000..cd13c41 --- /dev/null +++ b/352 Data Stream as Disjoint Intervals.py @@ -0,0 +1,62 @@ +""" +Given a data stream input of non-negative integers a1, a2, ..., an, ..., summarize the numbers seen so far as a list of +disjoint intervals. + +For example, suppose the integers from the data stream are 1, 3, 7, 2, 6, ..., then the summary will be: + +[1, 1] +[1, 1], [3, 3] +[1, 1], [3, 3], [7, 7] +[1, 3], [7, 7] +[1, 3], [6, 7] +Follow up: +What if there are lots of merges and the number of disjoint intervals are small compared to the data stream's size? +""" +__author__ = 'Daniel' + + +# Definition for an interval. +class Interval(object): + def __init__(self, s=0, e=0): + self.start = s + self.end = e + + +class SummaryRanges(object): + def __init__(self): + """ + BST is the most efficient, while heap is simple to write + Initialize your data structure here. + """ + self.itvls = [] + + def addNum(self, val): + """ + O(lg n) + :type val: int + :rtype: void + """ + self.itvls.append(Interval(val, val)) + + def getIntervals(self): + """ + O(n lg n) + :rtype: List[Interval] + """ + self.itvls.sort(key=lambda x: x.start) + + ret = [self.itvls[0]] + for itvl in self.itvls[1:]: + pre = ret[-1] + if itvl.start <= pre.end + 1: + pre.end = max(itvl.end, pre.end) + else: + ret.append(itvl) + + self.itvls = ret + return ret + +# Your SummaryRanges object will be instantiated and called as such: +# obj = SummaryRanges() +# obj.addNum(val) +# param_2 = obj.getIntervals() \ No newline at end of file From 93fe97fef7e929fdbdc25fbb53955d44e14ecff8 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 22 Oct 2016 11:50:22 -0700 Subject: [PATCH 226/585] update --- 015 Longest Common Prefix.py | 27 +++++++++++++++++++++------ 101 Symmetric Tree.py | 22 ++++++++++------------ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/015 Longest Common Prefix.py b/015 Longest Common Prefix.py index a83c64a..11bfdb1 100644 --- a/015 Longest Common Prefix.py +++ b/015 Longest Common Prefix.py @@ -2,8 +2,24 @@ Write a function to find the longest common prefix string amongst an array of strings. """ __author__ = 'Danyang' -class Solution: + + +class Solution(object): def longestCommonPrefix(self, strs): + if not strs: return "" + l = min(map(len, strs)) + i = 0 + while i < l: + char = strs[0][i] + for s in strs: + if s[i] != char: + return strs[0][:i] + + i += 1 + + return strs[0][:i] + + def longestCommonPrefixComplex(self, strs): """ O(k*n) :param strs: a list of string @@ -21,14 +37,14 @@ def longestCommonPrefix(self, strs): char = strs[0][i] j = 0 - while j read more on how binary tree is serialized on OJ. """ __author__ = 'Danyang' + + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: + +class Solution(object): def isSymmetric(self, root): """ dfs :param root: TreeNode :return: boolean """ - # Trivial if not root: return True return self.isSymmetrical(root.left, root.right) - - def isSymmetrical(self, mirror_left, mirror_right): - # Trivial - if not mirror_left and not mirror_right: + def isSymmetrical(self, l, r): + if not l and not r: return True # recursive - try: - if mirror_left.val==mirror_right.val and self.isSymmetrical(mirror_left.left, mirror_right.right) and self.isSymmetrical(mirror_left.right, mirror_right.left): - return True - except AttributeError: - return False + if (l and r and + l.val == r.val and self.isSymmetrical(l.left, r.right) and self.isSymmetrical(l.right, r.left)): + return True return False From 5b869120bb0d288715e0ccea81721ab7d8b0704c Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Tue, 1 Jan 2019 22:15:17 -0800 Subject: [PATCH 227/585] 412 --- 412 Fizz Buzz.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 412 Fizz Buzz.py diff --git a/412 Fizz Buzz.py b/412 Fizz Buzz.py new file mode 100644 index 0000000..771cb4d --- /dev/null +++ b/412 Fizz Buzz.py @@ -0,0 +1,51 @@ +""" +Write a program that outputs the string representation of numbers from 1 to n. + +But for multiples of three it should output "Fizz" instead of the number and for the multiples of five output "Buzz". +For numbers which are multiples of both three and five output "FizzBuzz". + +Example: + +n = 15, + +Return: +[ + "1", + "2", + "Fizz", + "4", + "Buzz", + "Fizz", + "7", + "8", + "Fizz", + "Buzz", + "11", + "Fizz", + "13", + "14", + "FizzBuzz" +] +""" +__author__ = 'Daniel' + + +class Solution(object): + def fizzBuzz(self, n): + """ + :type n: int + :rtype: List[str] + """ + ret = [] + for i in xrange(1, n+1): + cur = "" + if i % 3 == 0: + cur += "Fizz" + if i % 5 == 0: + cur += "Buzz" + if not cur: + cur = str(i) + + ret.append(cur) + + return ret \ No newline at end of file From a944043397be3495f2c9c6c477c0571e2c654dfa Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 2 Jan 2019 14:45:58 -0800 Subject: [PATCH 228/585] 414 427 --- 414 Third Maximum Number.py | 43 ++++++++++++++++++++++++++++ 427 Construct Quad Tree.py | 57 +++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 414 Third Maximum Number.py create mode 100644 427 Construct Quad Tree.py diff --git a/414 Third Maximum Number.py b/414 Third Maximum Number.py new file mode 100644 index 0000000..0d5c823 --- /dev/null +++ b/414 Third Maximum Number.py @@ -0,0 +1,43 @@ +#!/usr/bin/python3 +""" +Given a non-empty array of integers, return the third maximum number in this +array. If it does not exist, return the maximum number. The time complexity +must be in O(n). +""" +__author__ = 'Danyang' +import heapq + + +class Solution: + def thirdMax(self, nums): + """ + It is an easy question but error prone: + 1. Choice of min heap or max heap: use min heap (not max heap) because + we want to know the smallest maximum number + 2. Duplicate number + :type nums: List[int] + :rtype: int + """ + if not nums: + return None + + h = [] + for e in set(nums): + if len(h) < 3: + heapq.heappush(h, e) + elif len(h) == 3 and e > h[0]: + heapq.heappushpop(h, e) + + assert len(h) <= 3 + if len(h) == 3: + ret = min(h) + else: + ret = max(h) + return ret + + +if __name__ == "__main__": + assert Solution().thirdMax([1, 2, 3, 4]) == 2 + assert Solution().thirdMax([4, 3, 2, 1]) == 2 + assert Solution().thirdMax([2, 2, 3, 1]) == 1 + assert Solution().thirdMax([4, 3]) == 4 diff --git a/427 Construct Quad Tree.py b/427 Construct Quad Tree.py new file mode 100644 index 0000000..2dd16cc --- /dev/null +++ b/427 Construct Quad Tree.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +""" +We want to use quad trees to store an N x N boolean grid. Each cell in the grid +can only be true or false. The root node represents the whole grid. For each +node, it will be subdivided into four children nodes until the values in the +region it represents are all the same. + +Each node has another two boolean attributes : isLeaf and val. isLeaf is true if +and only if the node is a leaf node. The val attribute for a leaf node contains +the value of the region it represents. +""" +__author__ = 'Danyang' + + +# Definition for a QuadTree node. +class Node: + def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight): + self.val = val + self.isLeaf = isLeaf + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight + + +class Solution: + def construct(self, grid): + """ + DPS, check 4 children then merge + + :type grid: List[List[int]] + :rtype: Node + """ + l = len(grid) + return self._construct(grid, 0, 0, l) + + def _construct(self, grid, row, col, l): + """ + Use row col for matrix rather than x y coordiate since the direction is + error-prone + """ + if l == 1: + return Node(grid[row][col], True, None, None, None, None) + + l_child = l // 2 + topLeft = self._construct(grid, row, col, l_child) + topRight = self._construct(grid, row, col + l_child, l_child) + bottomLeft = self._construct(grid, row + l_child, col, l_child) + bottomRight = self._construct(grid, row + l_child, col + l_child, l_child) + is_leaf = ( + topLeft.val == topRight.val == bottomLeft.val == bottomRight.val + != "*" + ) + if is_leaf: + return Node(grid[row][col], True, None, None, None, None) + + return Node("*", False, topLeft, topRight, bottomLeft, bottomRight) From d34e28995731d3c6e65be3a0fe65811ea988d309 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 2 Jan 2019 15:16:00 -0800 Subject: [PATCH 229/585] 415 --- 415 Add Strings.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 415 Add Strings.py diff --git a/415 Add Strings.py b/415 Add Strings.py new file mode 100644 index 0000000..8140737 --- /dev/null +++ b/415 Add Strings.py @@ -0,0 +1,56 @@ +#!/usr/bin/python3 +""" +Given two non-negative integers num1 and num2 represented as string, return the sum of num1 and num2. + +Note: +The length of both num1 and num2 is < 5100. +Both num1 and num2 contains only digits 0-9. +Both num1 and num2 does not contain any leading zero. +You must not use any built-in BigInteger library or convert the inputs to integer directly. +""" + + +class Solution: + def int(self, n): + return ord(n) - ord("0") + + def addStrings(self, num1, num2): + """ + :type num1: str + :type num2: str + :rtype: str + """ + ret = [] + # let num2 to be one has more digit + if len(num1) > len(num2): + num1, num2 = num2, num1 + + num1 = num1[::-1] + num2 = num2[::-1] + carry = 0 + idx = 0 + while idx < len(num2): + if idx < len(num1): + s = self.int(num1[idx]) + self.int(num2[idx]) + carry + else: + s = self.int(num2[idx]) + carry + + if s >= 10: + s -= 10 + carry = 1 + else: + carry = 0 + + ret.append(s) + idx += 1 + + if carry: + ret.append(carry) + + return "".join(map(str, ret[::-1])) + + +if __name__ == "__main__": + assert Solution().addStrings("9999", "1") == "10000" + assert Solution().addStrings("9999", "9999") == "19998" + assert Solution().addStrings("23", "8") == "31" From 291bf967d49cabcbcb1460ba69d61f1d8fa200c7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 2 Jan 2019 15:29:50 -0800 Subject: [PATCH 230/585] 429 --- 429 N-ary Tree Level Order Traversal.py | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 429 N-ary Tree Level Order Traversal.py diff --git a/429 N-ary Tree Level Order Traversal.py b/429 N-ary Tree Level Order Traversal.py new file mode 100644 index 0000000..cacb5d3 --- /dev/null +++ b/429 N-ary Tree Level Order Traversal.py @@ -0,0 +1,37 @@ +#!/usr/bin/python3 +""" +Given an n-ary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). +""" + + +# Definition for a Node. +class Node: + def __init__(self, val, children): + self.val = val + self.children = children + + +class Solution: + def levelOrder(self, root): + """ + BFS + + :type root: Node + :rtype: List[List[int]] + """ + if not root: + return [] + + q = [root] + ret = [] + while q: + cur = [] + q_new = [] + for e in q: + q_new.extend(e.children) + cur.append(e.val) + + ret.append(cur) + q = q_new + + return ret From cc3e6c6727a302f012efafdf7acc85ee2597dd24 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 2 Jan 2019 15:42:11 -0800 Subject: [PATCH 231/585] 434 --- 434 Number of Segments in a String.py | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 434 Number of Segments in a String.py diff --git a/434 Number of Segments in a String.py b/434 Number of Segments in a String.py new file mode 100644 index 0000000..6de0c51 --- /dev/null +++ b/434 Number of Segments in a String.py @@ -0,0 +1,34 @@ +#!/usr/bin/python3 +""" +Count the number of segments in a string, where a segment is defined to be a +contiguous sequence of non-space characters. + +Please note that the string does not contain any non-printable characters. +""" + + +class Solution: + def countSegments(self, s): + """ + I could use split but it may not be the intention of this problem + + :type s: str + :rtype: int + """ + ret = 0 + if not s: + return ret + + # count at start + if s[0] != " ": + ret = 1 + prev = s[0] + for c in s[1:]: + if c != " " and prev == " ": + ret += 1 + prev = c + return ret + + +if __name__ == "__main__": + assert Solution().countSegments("Hello, my name is John") == 5 From a7a30b84960e0b69a725bb7e57c8751e0a3d4df7 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 2 Jan 2019 17:19:22 -0800 Subject: [PATCH 232/585] 437 --- 437 Path Sum III.py | 54 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 437 Path Sum III.py diff --git a/437 Path Sum III.py b/437 Path Sum III.py new file mode 100644 index 0000000..f28fea7 --- /dev/null +++ b/437 Path Sum III.py @@ -0,0 +1,54 @@ +#!/usr/bin/python3 +""" +You are given a binary tree in which each node contains an integer value. + +Find the number of paths that sum to a given value. + +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). + +The tree has no more than 1,000 nodes and the values are in the range -1,000,000 +to 1,000,000. +""" +# Definition for a binary tree node. +class TreeNode: + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution: + def pathSum(self, root, sum): + """ + Brute force: two dfs, O(n^2) + + Prefix sum in Tree, starting from root - O(n) + :type root: TreeNode + :type sum: int + :rtype: int + """ + count = [0] # pass as a reference + self.dfs(root, sum, 0, {}, count) + return count[0] + + def dfs(self, root, sum, cur_sum, prefix_sum, count): + """ + Root to node sum + prefix_sum: Dict[int, int], sum -> count + """ + if not root: + return + + cur_sum += root.val + prefix_sum[cur_sum] = prefix_sum.get(cur_sum, 0) + 1 + # ∃ prefix_sum: cur_sum - prefix_sum = sum + diff = cur_sum - sum + if diff in prefix_sum: + count[0] += prefix_sum[diff] + if diff == 0: # trivial case + count[0] += 1 + + self.dfs(root.left, sum, cur_sum, prefix_sum, count) + self.dfs(root.right, sum, cur_sum, prefix_sum, count) + prefix_sum[cur_sum] -= 1 # pop to save space From 499c12dd40f75e07e816cdd335c0ecd61e1ee135 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 2 Jan 2019 17:58:03 -0800 Subject: [PATCH 233/585] 438 --- 437 Path Sum III.py | 2 +- 438 Find All Anagrams in a String.py | 43 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 438 Find All Anagrams in a String.py diff --git a/437 Path Sum III.py b/437 Path Sum III.py index f28fea7..1fa2da8 100644 --- a/437 Path Sum III.py +++ b/437 Path Sum III.py @@ -41,7 +41,6 @@ def dfs(self, root, sum, cur_sum, prefix_sum, count): return cur_sum += root.val - prefix_sum[cur_sum] = prefix_sum.get(cur_sum, 0) + 1 # ∃ prefix_sum: cur_sum - prefix_sum = sum diff = cur_sum - sum if diff in prefix_sum: @@ -49,6 +48,7 @@ def dfs(self, root, sum, cur_sum, prefix_sum, count): if diff == 0: # trivial case count[0] += 1 + prefix_sum[cur_sum] = prefix_sum.get(cur_sum, 0) + 1 self.dfs(root.left, sum, cur_sum, prefix_sum, count) self.dfs(root.right, sum, cur_sum, prefix_sum, count) prefix_sum[cur_sum] -= 1 # pop to save space diff --git a/438 Find All Anagrams in a String.py b/438 Find All Anagrams in a String.py new file mode 100644 index 0000000..01f1939 --- /dev/null +++ b/438 Find All Anagrams in a String.py @@ -0,0 +1,43 @@ +#!/usr/bin/python3 +""" +Given a string s and a non-empty string p, find all the start indices of p's anagrams in s. + +Strings consists of lowercase English letters only and the length of both strings s and p will not be larger than 20,100. + +The order of output does not matter. +""" +from collections import Counter + + +class Solution: + def findAnagrams(self, s, target): + """ + Brute force: O(|target|) * O(cmp) * O(|s|) + Counter: O(cmp) * O(|s|) + where O(cmp) = 26, the length of alphabeta + :type s: str + :type p: str + :rtype: List[int] + """ + ret = [] + counter_target = Counter(target) + counter_cur = Counter(s[:len(target)]) + if counter_cur == counter_target: + ret.append(0) + + for idx in range(len(target), len(s)): + head = s[idx - len(target)] + tail = s[idx] + counter_cur[tail] += 1 + counter_cur[head] -= 1 + if counter_cur[head] == 0: + del counter_cur[head] # requried for comparison + if counter_cur == counter_target: + # idx is the ending index, find the starting + ret.append(idx - len(target) + 1) + + return ret + + +if __name__ == "__main__": + assert Solution().findAnagrams("cbaebabacd", "abc") == [0, 6] From 2500eda82cf84d166eba022472bcf72ba3cf9440 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 2 Jan 2019 21:44:13 -0800 Subject: [PATCH 234/585] 441 --- 441 Arranging Coins.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 441 Arranging Coins.py diff --git a/441 Arranging Coins.py b/441 Arranging Coins.py new file mode 100644 index 0000000..a491ef6 --- /dev/null +++ b/441 Arranging Coins.py @@ -0,0 +1,26 @@ +#!/usr/bin/python3 +""" +You have a total of n coins that you want to form in a staircase shape, where every k-th row must have exactly k coins. + +Given n, find the total number of full staircase rows that can be formed. + +n is a non-negative integer and fits within the range of a 32-bit signed integer. +""" + + +class Solution: + def arrangeCoins(self, n): + """ + Solve a math equation + (1+r)r/2 <= n + :type n: int + :rtype: int + """ + return int( + (2*n + 1/4)**(1/2) - 1/2 + ) + + +if __name__ == "__main__": + assert Solution().arrangeCoins(5) == 2 + assert Solution().arrangeCoins(8) == 3 From 449eec8dd04425ad0c8496df7cc5e21d225671d6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Wed, 2 Jan 2019 22:22:31 -0800 Subject: [PATCH 235/585] 448 --- ...ind All Numbers Disappeared in an Array.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 448 Find All Numbers Disappeared in an Array.py diff --git a/448 Find All Numbers Disappeared in an Array.py b/448 Find All Numbers Disappeared in an Array.py new file mode 100644 index 0000000..cfc9c94 --- /dev/null +++ b/448 Find All Numbers Disappeared in an Array.py @@ -0,0 +1,37 @@ +#!/usr/bin/python3 +""" +Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once. + +Find all the elements of [1, n] inclusive that do not appear in this array. + +Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space. +""" + + +class Solution: + def findDisappearedNumbers(self, A): + """ + You can use hash map with extra space O(n). + To use without extra space, notice the additional constraints that: + 1. 1 ≤ a[i] ≤ n + 2. appear twice or once + => use original array as storage with a[i] (- 1) as the index + :type A: List[int] + :rtype: List[int] + """ + for idx in range(len(A)): + while True: + target = A[idx] - 1 + if idx == target or A[idx] == A[target]: + break + A[idx], A[target] = A[target], A[idx] + + missing = [] + for idx, elm in enumerate(A): + if idx != elm - 1: + missing.append(idx + 1) + return missing + + +if __name__ == "__main__": + assert Solution().findDisappearedNumbers([4, 3, 2, 7, 8, 2, 3, 1]) == [5, 6] From 83988979df924e1b25bbdfac11828daec041816e Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 3 Jan 2019 12:46:04 -0800 Subject: [PATCH 236/585] 447 --- 447 Number of Boomerangs.py | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 447 Number of Boomerangs.py diff --git a/447 Number of Boomerangs.py b/447 Number of Boomerangs.py new file mode 100644 index 0000000..c38ae7d --- /dev/null +++ b/447 Number of Boomerangs.py @@ -0,0 +1,60 @@ +#!/usr/bin/python3 +""" +Given n points in the plane that are all pairwise distinct, a "boomerang" is a +tuple of points (i, j, k) such that the distance between i and j equals the +distance between i and k (the order of the tuple matters). + +Find the number of boomerangs. You may assume that n will be at most 500 and +coordinates of points are all in the range [-10000, 10000] (inclusive). +""" +from collections import Counter + + +class Solution: + def distance(self, a, b): + return (a[0] - b[0])**2 + (a[1] - b[1])**2 + + def numberOfBoomerangs(self, points): + """ + Reverse look up + :type points: List[List[int]] + :rtype: int + """ + ret = 0 + for i in range(len(points)): + dist_cnt = Counter() + for j in range(len(points)): + if i != j: + d = self.distance(points[i], points[j]) + dist_cnt[d] += 1 + + for v in dist_cnt.values(): + # Permutation: P v 2 + ret += v * (v - 1) + + return ret + + def numberOfBoomerangs_TLE(self, points): + """ + Reverse look up + :type points: List[List[int]] + :rtype: int + """ + ret = 0 + for i in range(len(points)): + dist_cnt = Counter() + dist_lst = [] + for j in range(len(points)): + if i != j: + d = self.distance(points[i], points[j]) + dist_lst.append(d) + dist_cnt[d] += 1 + + for d in dist_lst: + ret += (dist_cnt[d] - 1) + + return ret + + +if __name__ == "__main__": + assert Solution().numberOfBoomerangs([[0,0],[1,0],[2,0]]) == 2 From c9e6937ca15630286a7e221511530d070657fadf Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Thu, 3 Jan 2019 13:17:32 -0800 Subject: [PATCH 237/585] 455 --- 453 Minimum Moves to Equal Array Elements.py | 24 +++++++++++ 455 Assign Cookies.py | 42 ++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 453 Minimum Moves to Equal Array Elements.py create mode 100644 455 Assign Cookies.py diff --git a/453 Minimum Moves to Equal Array Elements.py b/453 Minimum Moves to Equal Array Elements.py new file mode 100644 index 0000000..a15ca5e --- /dev/null +++ b/453 Minimum Moves to Equal Array Elements.py @@ -0,0 +1,24 @@ +#!/usr/bin/python3 +""" +Given a non-empty integer array of size n, find the minimum number of moves +required to make all array elements equal, where a move is incrementing n - 1 +elements by 1. +""" + + +class Solution: + def minMoves(self, nums): + """ + List out, find the pattern + for every operation, the max number does not change, then bring the min + number 1 step closer to the max. + + :type nums: List[int] + :rtype: int + """ + mini = min(nums) + return sum(map(lambda e: e - mini, nums)) + + +if __name__ == "__main__": + assert Solution().minMoves([1, 2, 3]) == 3 diff --git a/455 Assign Cookies.py b/455 Assign Cookies.py new file mode 100644 index 0000000..39f9908 --- /dev/null +++ b/455 Assign Cookies.py @@ -0,0 +1,42 @@ +#!/usr/bin/python3 +""" +Assume you are an awesome parent and want to give your children some cookies. +But, you should give each child at most one cookie. Each child i has a greed +factor gi, which is the minimum size of a cookie that the child will be content +with; and each cookie j has a size sj. If sj >= gi, we can assign the cookie j +to the child i, and the child i will be content. Your goal is to maximize the +number of your content children and output the maximum number. + +Note: +You may assume the greed factor is always positive. +You cannot assign more than one cookie to one child. +""" + + +class Solution: + def findContentChildren(self, g, s): + """ + Greedy + + :type g: List[int] + :type s: List[int] + :rtype: int + """ + g.sort() + s.sort() + ret = 0 + i = 0 + j = 0 + while i < len(g) and j < len(s): + if g[i] <= s[j]: + ret += 1 + i += 1 + j += 1 + else: + j += 1 + + return ret + + +if __name__ == "__main__": + assert Solution().findContentChildren([10,9,8,7], [5,6,7,8]) == 2 From ed96644d83fd6128809500a7b869f0743be2c38f Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 4 Jan 2019 12:05:22 -0800 Subject: [PATCH 238/585] 459 --- 459 Repeated Substring Pattern.py | 61 +++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 459 Repeated Substring Pattern.py diff --git a/459 Repeated Substring Pattern.py b/459 Repeated Substring Pattern.py new file mode 100644 index 0000000..6b0bde1 --- /dev/null +++ b/459 Repeated Substring Pattern.py @@ -0,0 +1,61 @@ +#!/usr/bin/python3 +""" +Given a non-empty string check if it can be constructed by taking a substring +of it and appending multiple copies of the substring together. You may assume +the given string consists of lowercase English letters only and its length will +not exceed 10000. +""" + + +class Solution: + def repeatedSubstringPattern(self, s): + """ + The start of the substring is always 0, then incr the ending index e + until n/2 where n = len(s) + Brute force: O(n/2) * O(n) + + test substring using KMP is O(|target|) + + if s is composed of n substrings p, then s2 = s + s should contain + 2n * p. + + Destroying the first and the last character leads to at + least (2n - 2) * p left. + + n >= 2 + 2n - 2 >= n + S1[1:-1] should still contain S + :type s: str + :rtype: bool + """ + return s in (s + s)[1:-1] + + def repeatedSubstringPattern_error(self, s): + """ + Two pointers algorithm. The start of the substring is always 0 + :type s: str + :rtype: bool + """ + if not s: + return False + p1 = 0 + e = 1 # ending s[0:e] is the substring + p2 = 1 + while p2 < len(s): + if s[p1] == s[p2]: + p1 += 1 + if p1 == e: + p1 = 0 + else: + p1 = 0 + e = p2 + 1 + + p2 += 1 + + return p2 == len(s) and p1 == 0 and e != len(s) + + +if __name__ == "__main__": + assert Solution().repeatedSubstringPattern("abab") == True + assert Solution().repeatedSubstringPattern("abcd") == False + assert Solution().repeatedSubstringPattern("abacababacab") == True From 79101780ba10fc718c59278a4e6745587a50bc4b Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Fri, 4 Jan 2019 12:59:00 -0800 Subject: [PATCH 239/585] 463 --- 463 Island Perimeter.py | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 463 Island Perimeter.py diff --git a/463 Island Perimeter.py b/463 Island Perimeter.py new file mode 100644 index 0000000..903dee0 --- /dev/null +++ b/463 Island Perimeter.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 +""" +You are given a map in form of a two-dimensional integer grid where 1 represents +land and 0 represents water. + +Grid cells are connected horizontally/vertically (not diagonally). The grid is +completely surrounded by water, and there is exactly one island (i.e., one or +more connected land cells). + +The island doesn't have "lakes" (water inside that isn't connected to the water +around the island). One cell is a square with side length 1. The grid is +rectangular, width and height don't exceed 100. Determine the perimeter of the island. +""" +class Solution: + dirs = [(0, -1), (-1, 0), (0, 1), (1, 0)] + + def islandPerimeter(self, grid): + """ + There is constraint that one concrete island + + check surrounding: O(4) * O(n) = O(n) + count side for land + :type grid: List[List[int]] + :rtype: int + """ + ret = 0 + if not grid: + return ret + R = len(grid) + C = len(grid[0]) + for r0 in range(R): + for c0 in range(C): + if grid[r0][c0] == 1: + for dr, dc in self.dirs: + r = r0 + dr + c = c0 + dc + if r < 0 or r >= R or c < 0 or c >= C: + ret += 1 + elif grid[r][c] == 0: + ret += 1 + + return ret + + +if __name__ == "__main__": + grid = [ + [0,1,0,0], + [1,1,1,0], + [0,1,0,0], + [1,1,0,0], + ] + assert Solution().islandPerimeter(grid) == 16 From 195fd0d0047b39a0ad514a5b86a4a792bfb0f930 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 5 Jan 2019 12:09:25 -0800 Subject: [PATCH 240/585] 443 --- 443 String Compression.py | 67 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 443 String Compression.py diff --git a/443 String Compression.py b/443 String Compression.py new file mode 100644 index 0000000..74cc323 --- /dev/null +++ b/443 String Compression.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +""" +Given an array of characters, compress it in-place. + +The length after compression must always be smaller than or equal to the original array. + +Every element of the array should be a character (not int) of length 1. + +After you are done modifying the input array in-place, return the new length of the array. + + +Follow up: +Could you solve it using only O(1) extra space? +""" + + +class Solution: + def compress(self, chars): + """ + tedious pointer manipulation + :type chars: List[str] + :rtype: int + """ + ret = 1 + s = 0 # start index of current char + for i in range(1, len(chars) + 1): + if i < len(chars) and chars[i] == chars[s]: + continue + l = i - s + if l > 1: + for digit in str(l): + chars[ret] = digit + ret += 1 + if i < len(chars): + chars[ret] = chars[i] + ret += 1 + s = i + + return ret + + def compress_error(self, chars): + """ + tedious pointer manipulation + :type chars: List[str] + :rtype: int + """ + s = 0 + for idx in range(1, len(chars) + 1): + if idx < len(chars) and chars[idx] == chars[s]: + continue + l = idx - s + if l == 1: + s = min(s + 1, len(chars) - 1) + else: + for digit in str(l): + s += 1 + chars[s] = digit + if idx < len(chars): + s += 1 + chars[s] = chars[idx] + return s + 1 + + +if __name__ == "__main__": + assert Solution().compress(["a"]) == 1 + assert Solution().compress(["a","a","b","b","c","c","c"]) == 6 + assert Solution().compress(["a","b","b","b","b","b","b","b","b","b","b","b","b"]) == 4 From fedcc8fb77574ea2e0412fca5f40929df38db3d6 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sat, 5 Jan 2019 14:15:54 -0800 Subject: [PATCH 241/585] 475 461 --- 461 Hamming Distance.py | 31 +++++++++++++++++++ 475 Heaters.py | 68 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 461 Hamming Distance.py create mode 100644 475 Heaters.py diff --git a/461 Hamming Distance.py b/461 Hamming Distance.py new file mode 100644 index 0000000..98ec967 --- /dev/null +++ b/461 Hamming Distance.py @@ -0,0 +1,31 @@ +#!/usr/bin/python3 +""" +The Hamming distance between two integers is the number of positions at which +the corresponding bits are different. + +Given two integers x and y, calculate the Hamming distance. + +Note: +0 ≤ x, y < 2^31. +""" + + +class Solution: + def hammingDistance(self, x, y): + """ + :type x: int + :type y: int + :rtype: int + """ + diff = x ^ y + ret = 0 + while diff: + ret += diff & 1 + diff >>= 1 + + return ret + + +if __name__ == "__main__": + assert Solution().hammingDistance(3, 1) == 1 + assert Solution().hammingDistance(1, 4) == 2 diff --git a/475 Heaters.py b/475 Heaters.py new file mode 100644 index 0000000..12ed40d --- /dev/null +++ b/475 Heaters.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 +""" +Winter is coming! Your first job during the contest is to design a standard +heater with fixed warm radius to warm all the houses. + +Now, you are given positions of houses and heaters on a horizontal line, find +out minimum radius of heaters so that all houses could be covered by those +heaters. + +So, your input will be the positions of houses and heaters seperately, and your +expected output will be the minimum radius standard of heaters. + +Note: +Numbers of houses and heaters you are given are non-negative and will not exceed 25000. +Positions of houses and heaters you are given are non-negative and will not exceed 10^9. +As long as a house is in the heaters' warm radius range, it can be warmed. +All the heaters follow your radius standard and the warm radius will the same. +""" +import bisect + + +class Solution: + def findRadius(self, houses, heaters): + """ + check the responsibility + use bisect + :type houses: List[int] + :type heaters: List[int] + :rtype: int + """ + houses.sort() + heaters.sort() + r = 0 + i = 0 + for h in houses: + i = bisect.bisect(heaters, h) # insertion point + left = max(0, i - 1) + right = min(len(heaters) - 1, i) + r_cur = min(abs(heaters[left] - h), abs(heaters[right] - h)) + r = max(r, r_cur) + + return r + + def findRadius_naive(self, houses, heaters): + """ + check the responsibility + :type houses: List[int] + :type heaters: List[int] + :rtype: int + """ + houses.sort() + heaters.sort() + heaters.append(float('inf')) + r = 0 + i = 0 + for h in houses: + # possible bisect + while h > (heaters[i] + heaters[i+1]) / 2: + # find which heater is responsible for the house + i += 1 + + r = max(r, abs(heaters[i] - h)) + + return r + + +if __name__ == "__main__": + assert Solution().findRadius([1,2,3,4], [1,4]) == 1 From 5de975b9cf786fe9ba36c20b5067f8ca14451207 Mon Sep 17 00:00:00 2001 From: "Daniel D. ZHANG" Date: Sun, 6 Jan 2019 14:59:33 -0800 Subject: [PATCH 242/585] 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 243/585] 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 244/585] 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 245/585] 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 246/585] 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 247/585] 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 248/585] 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 249/585] 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 250/585] 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 251/585] 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 252/585] 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 253/585] 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 254/585] 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 255/585] 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 256/585] 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 257/585] 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 258/585] 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 259/585] 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 260/585] 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 261/585] 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 262/585] 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 263/585] 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 264/585] 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 265/585] 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 266/585] 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 267/585] 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 268/585] 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 269/585] 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 270/585] 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 271/585] 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 272/585] 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 273/585] 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 274/585] 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 275/585] 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 276/585] 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 277/585] 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 278/585] 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 279/585] 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 280/585] 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 281/585] 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 282/585] 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 283/585] 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 284/585] 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 285/585] 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 286/585] 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 287/585] 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 288/585] 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 289/585] 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 290/585] 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 291/585] 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 292/585] 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 293/585] 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 294/585] 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 295/585] 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 296/585] 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 297/585] 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 298/585] 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 299/585] 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 300/585] 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 301/585] 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 302/585] 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 303/585] 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 304/585] 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 305/585] 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 306/585] 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 307/585] 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 308/585] 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 309/585] 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 310/585] 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 311/585] 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 312/585] 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 313/585] 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 314/585] 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 315/585] 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 316/585] 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 317/585] 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 318/585] 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 319/585] 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 320/585] 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 321/585] 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 322/585] 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 323/585] 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 324/585] 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 325/585] 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 326/585] 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 327/585] 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 328/585] 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 329/585] 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 330/585] 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 331/585] 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 332/585] 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 333/585] 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 334/585] 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 335/585] 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 336/585] 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 337/585] 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 338/585] 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 339/585] 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 340/585] 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 341/585] 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 342/585] 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 343/585] 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 344/585] 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 345/585] 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 346/585] 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 347/585] 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 348/585] 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 349/585] 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 350/585] 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 351/585] 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 352/585] 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 353/585] 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 354/585] 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 355/585] 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 356/585] 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 357/585] 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 358/585] 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 359/585] 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 360/585] 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 361/585] 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 362/585] 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 363/585] 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 364/585] 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 365/585] 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 366/585] 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 367/585] 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 368/585] 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 369/585] 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 370/585] 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 371/585] 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 372/585] 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 373/585] 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 374/585] 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 375/585] 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 376/585] 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 377/585] 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 378/585] 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 379/585] 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 380/585] 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 381/585] 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 382/585] 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 383/585] 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 384/585] 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 385/585] 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 386/585] 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 387/585] 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 388/585] 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 389/585] 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 390/585] 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 391/585] 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 392/585] 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 393/585] 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 394/585] 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 395/585] 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 396/585] 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 397/585] 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 398/585] 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 399/585] 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 400/585] 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 401/585] 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 402/585] 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 403/585] 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 404/585] 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 405/585] 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 406/585] 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 407/585] 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 408/585] 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 409/585] 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 410/585] 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 411/585] 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 412/585] 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 413/585] 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 414/585] 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 415/585] 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 416/585] 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 417/585] 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 418/585] 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 419/585] 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 420/585] 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 421/585] 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 422/585] 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 423/585] 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 424/585] 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 425/585] 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 426/585] 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 427/585] 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 428/585] 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 429/585] 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 430/585] 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 431/585] 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 432/585] 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 433/585] 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 434/585] 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 435/585] 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 436/585] 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 437/585] 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 438/585] 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 439/585] 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 440/585] 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 441/585] 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 442/585] 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 443/585] 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 444/585] 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 445/585] 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 446/585] 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 447/585] 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 448/585] 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 449/585] 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 450/585] 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 451/585] 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 452/585] 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 453/585] 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 454/585] 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 455/585] 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 456/585] 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 457/585] 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 458/585] 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 459/585] 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 460/585] 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 461/585] 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 462/585] 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 463/585] 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 464/585] 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 465/585] 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 466/585] 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 467/585] 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 468/585] 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 469/585] 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 470/585] 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 471/585] 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 472/585] 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 473/585] 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 474/585] 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 475/585] 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 476/585] 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 477/585] 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 478/585] 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 479/585] 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 480/585] 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 481/585] 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 482/585] 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 483/585] 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 484/585] 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 485/585] 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 486/585] 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 487/585] 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 488/585] 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 489/585] 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 490/585] 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 491/585] 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 492/585] 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 493/585] 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 494/585] 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 495/585] 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 496/585] 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 497/585] 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 498/585] 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 499/585] 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 500/585] 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 501/585] 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 502/585] 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 503/585] 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 504/585] 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 505/585] 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 506/585] 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 507/585] 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 508/585] 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 509/585] 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 510/585] 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 511/585] 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 512/585] 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 513/585] 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 514/585] 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 515/585] 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 516/585] 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 517/585] 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 518/585] 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 519/585] 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 520/585] 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 521/585] 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 522/585] 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 523/585] 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 524/585] 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 525/585] 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 526/585] 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 527/585] 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 528/585] 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 529/585] 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 530/585] 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 531/585] 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 532/585] 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 533/585] 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 534/585] 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 535/585] 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 536/585] 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 537/585] 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 538/585] 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 539/585] 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 540/585] 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 541/585] 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 542/585] 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 543/585] 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 544/585] 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 545/585] 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 546/585] 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 547/585] 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 548/585] 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 549/585] 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 550/585] 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 551/585] 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 552/585] 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 553/585] 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 554/585] 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 555/585] 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 556/585] 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 557/585] 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 558/585] 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 559/585] 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 560/585] 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 561/585] 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 562/585] 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 563/585] 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 564/585] 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 565/585] 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 566/585] 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 567/585] 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 568/585] 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 569/585] 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 570/585] 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 571/585] 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 572/585] 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 573/585] 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 574/585] 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 575/585] 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 576/585] 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 577/585] 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 578/585] 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 579/585] 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 580/585] 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 581/585] 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 582/585] 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 583/585] 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 584/585] 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 585/585] 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