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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 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/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 + + + + 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/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 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/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/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 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/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 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/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 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/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/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 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/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 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 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. + """ 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 pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy