diff --git a/solution/2200-2299/2276.Count Integers in Intervals/README.md b/solution/2200-2299/2276.Count Integers in Intervals/README.md index e6816beea5747..21289728137c0 100644 --- a/solution/2200-2299/2276.Count Integers in Intervals/README.md +++ b/solution/2200-2299/2276.Count Integers in Intervals/README.md @@ -99,46 +99,84 @@ countIntervals.count(); // 返回 8 ```python class Node: - def __init__(self): - self.tag = 0 - self.tot = 0 + __slots__ = ("left", "right", "l", "r", "mid", "v", "add") + + def __init__(self, l, r): self.left = None self.right = None + self.l = l + self.r = r + self.mid = (l + r) // 2 + self.v = 0 + self.add = 0 + - def update(self, l, r, a, b): - if self.tag == 1: +class SegmentTree: + def __init__(self): + self.root = Node(1, int(1e9) + 1) + + def modify(self, l, r, v, node=None): + if node is None: + node = self.root + if l > r: return - mid = (a + b) >> 1 - if l == a and r == b: - self.tag = 1 - self.tot = b - a + 1 + if node.l >= l and node.r <= r: + node.v = node.r - node.l + 1 + node.add = v return - if not self.left: - self.left = Node() - if not self.right: - self.right = Node() - if mid >= l: - self.left.update(l, min(mid, r), a, mid) - if mid + 1 <= r: - self.right.update(max(mid + 1, l), r, mid + 1, b) - self.tag = 0 - self.tot = self.left.tot + self.right.tot + self.pushdown(node) + if l <= node.mid: + self.modify(l, r, v, node.left) + if r > node.mid: + self.modify(l, r, v, node.right) + self.pushup(node) + + def query(self, l, r, node=None): + if node is None: + node = self.root + if l > r: + return 0 + if node.l >= l and node.r <= r: + return node.v + self.pushdown(node) + v = 0 + if l <= node.mid: + v += self.query(l, r, node.left) + if r > node.mid: + v += self.query(l, r, node.right) + return v + + def pushup(self, node): + node.v = node.left.v + node.right.v + + def pushdown(self, node): + if node.left is None: + node.left = Node(node.l, node.mid) + if node.right is None: + node.right = Node(node.mid + 1, node.r) + if node.add != 0: + left, right = node.left, node.right + left.add = node.add + right.add = node.add + left.v = left.r - left.l + 1 + right.v = right.r - right.l + 1 + node.add = 0 class CountIntervals: def __init__(self): - self.tree = Node() + self.tree = SegmentTree() - def add(self, left: int, right: int) -> None: - self.tree.update(left, right, 0, 1000000010) + def add(self, left, right): + self.tree.modify(left, right, 1) - def count(self) -> int: - return self.tree.tot + def count(self): + return self.tree.query(1, int(1e9)) # Your CountIntervals object will be instantiated and called as such: # obj = CountIntervals() -# obj.add(left,right) +# obj.add(left, right) # param_2 = obj.count() ``` @@ -548,99 +586,4 @@ class CountIntervals { - - -### 方法二 - - - -#### Python3 - -```python -class Node: - __slots__ = ("left", "right", "l", "r", "mid", "v", "add") - - def __init__(self, l, r): - self.left = None - self.right = None - self.l = l - self.r = r - self.mid = (l + r) // 2 - self.v = 0 - self.add = 0 - - -class SegmentTree: - def __init__(self): - self.root = Node(1, int(1e9) + 1) - - def modify(self, l, r, v, node=None): - if node is None: - node = self.root - if l > r: - return - if node.l >= l and node.r <= r: - node.v = node.r - node.l + 1 - node.add = v - return - self.pushdown(node) - if l <= node.mid: - self.modify(l, r, v, node.left) - if r > node.mid: - self.modify(l, r, v, node.right) - self.pushup(node) - - def query(self, l, r, node=None): - if node is None: - node = self.root - if l > r: - return 0 - if node.l >= l and node.r <= r: - return node.v - self.pushdown(node) - v = 0 - if l <= node.mid: - v += self.query(l, r, node.left) - if r > node.mid: - v += self.query(l, r, node.right) - return v - - def pushup(self, node): - node.v = node.left.v + node.right.v - - def pushdown(self, node): - if node.left is None: - node.left = Node(node.l, node.mid) - if node.right is None: - node.right = Node(node.mid + 1, node.r) - if node.add != 0: - left, right = node.left, node.right - left.add = node.add - right.add = node.add - left.v = left.r - left.l + 1 - right.v = right.r - right.l + 1 - node.add = 0 - - -class CountIntervals: - def __init__(self): - self.tree = SegmentTree() - - def add(self, left, right): - self.tree.modify(left, right, 1) - - def count(self): - return self.tree.query(1, int(1e9)) - - -# Your CountIntervals object will be instantiated and called as such: -# obj = CountIntervals() -# obj.add(left, right) -# param_2 = obj.count() -``` - - - - - diff --git a/solution/2200-2299/2276.Count Integers in Intervals/README_EN.md b/solution/2200-2299/2276.Count Integers in Intervals/README_EN.md index b446c7e66e4d0..445284195613b 100644 --- a/solution/2200-2299/2276.Count Integers in Intervals/README_EN.md +++ b/solution/2200-2299/2276.Count Integers in Intervals/README_EN.md @@ -48,7 +48,7 @@ tags: [null, null, null, 6, null, 8] Explanation -CountIntervals countIntervals = new CountIntervals(); // initialize the object with an empty set of intervals. +CountIntervals countIntervals = new CountIntervals(); // initialize the object with an empty set of intervals. countIntervals.add(2, 3); // add [2, 3] to the set of intervals. countIntervals.add(7, 10); // add [7, 10] to the set of intervals. countIntervals.count(); // return 6 @@ -77,7 +77,20 @@ countIntervals.count(); // return 8 -### Solution 1 +### Solution 1: Segment Tree (Dynamic Opening) + +According to the problem description, we need to maintain a set of intervals that supports adding intervals and querying operations. For adding intervals, we can use a segment tree to maintain the interval set. + +The segment tree divides the entire interval into multiple non-contiguous sub-intervals, with the number of sub-intervals not exceeding $\log(width)$. To update the value of an element, we only need to update $\log(width)$ intervals, and these intervals are all contained within a larger interval that includes the element. When modifying intervals, we need to use **lazy propagation** to ensure efficiency. + +- Each node of the segment tree represents an interval; +- The segment tree has a unique root node representing the entire range, such as $[1, N]$; +- Each leaf node of the segment tree represents a unit interval of length 1, $[x, x]$; +- For each internal node $[l, r]$, its left child is $[l, mid]$ and its right child is $[mid+1, r]$, where $mid = \lfloor (l + r) / 2 \rfloor$ (i.e., floor division). + +Since the data range in the problem is large, we can use a dynamically opened segment tree. A dynamically opened segment tree means that we only open nodes when needed, rather than opening all nodes at the beginning, which saves space. + +In terms of time complexity, each operation has a time complexity of $O(\log n)$. The space complexity is $O(m \times \log n)$, where $m$ is the number of operations and $n$ is the data range. @@ -85,46 +98,84 @@ countIntervals.count(); // return 8 ```python class Node: - def __init__(self): - self.tag = 0 - self.tot = 0 + __slots__ = ("left", "right", "l", "r", "mid", "v", "add") + + def __init__(self, l, r): self.left = None self.right = None + self.l = l + self.r = r + self.mid = (l + r) // 2 + self.v = 0 + self.add = 0 + + +class SegmentTree: + def __init__(self): + self.root = Node(1, int(1e9) + 1) - def update(self, l, r, a, b): - if self.tag == 1: + def modify(self, l, r, v, node=None): + if node is None: + node = self.root + if l > r: return - mid = (a + b) >> 1 - if l == a and r == b: - self.tag = 1 - self.tot = b - a + 1 + if node.l >= l and node.r <= r: + node.v = node.r - node.l + 1 + node.add = v return - if not self.left: - self.left = Node() - if not self.right: - self.right = Node() - if mid >= l: - self.left.update(l, min(mid, r), a, mid) - if mid + 1 <= r: - self.right.update(max(mid + 1, l), r, mid + 1, b) - self.tag = 0 - self.tot = self.left.tot + self.right.tot + self.pushdown(node) + if l <= node.mid: + self.modify(l, r, v, node.left) + if r > node.mid: + self.modify(l, r, v, node.right) + self.pushup(node) + + def query(self, l, r, node=None): + if node is None: + node = self.root + if l > r: + return 0 + if node.l >= l and node.r <= r: + return node.v + self.pushdown(node) + v = 0 + if l <= node.mid: + v += self.query(l, r, node.left) + if r > node.mid: + v += self.query(l, r, node.right) + return v + + def pushup(self, node): + node.v = node.left.v + node.right.v + + def pushdown(self, node): + if node.left is None: + node.left = Node(node.l, node.mid) + if node.right is None: + node.right = Node(node.mid + 1, node.r) + if node.add != 0: + left, right = node.left, node.right + left.add = node.add + right.add = node.add + left.v = left.r - left.l + 1 + right.v = right.r - right.l + 1 + node.add = 0 class CountIntervals: def __init__(self): - self.tree = Node() + self.tree = SegmentTree() - def add(self, left: int, right: int) -> None: - self.tree.update(left, right, 0, 1000000010) + def add(self, left, right): + self.tree.modify(left, right, 1) - def count(self) -> int: - return self.tree.tot + def count(self): + return self.tree.query(1, int(1e9)) # Your CountIntervals object will be instantiated and called as such: # obj = CountIntervals() -# obj.add(left,right) +# obj.add(left, right) # param_2 = obj.count() ``` @@ -534,99 +585,4 @@ class CountIntervals { - - -### Solution 2 - - - -#### Python3 - -```python -class Node: - __slots__ = ("left", "right", "l", "r", "mid", "v", "add") - - def __init__(self, l, r): - self.left = None - self.right = None - self.l = l - self.r = r - self.mid = (l + r) // 2 - self.v = 0 - self.add = 0 - - -class SegmentTree: - def __init__(self): - self.root = Node(1, int(1e9) + 1) - - def modify(self, l, r, v, node=None): - if node is None: - node = self.root - if l > r: - return - if node.l >= l and node.r <= r: - node.v = node.r - node.l + 1 - node.add = v - return - self.pushdown(node) - if l <= node.mid: - self.modify(l, r, v, node.left) - if r > node.mid: - self.modify(l, r, v, node.right) - self.pushup(node) - - def query(self, l, r, node=None): - if node is None: - node = self.root - if l > r: - return 0 - if node.l >= l and node.r <= r: - return node.v - self.pushdown(node) - v = 0 - if l <= node.mid: - v += self.query(l, r, node.left) - if r > node.mid: - v += self.query(l, r, node.right) - return v - - def pushup(self, node): - node.v = node.left.v + node.right.v - - def pushdown(self, node): - if node.left is None: - node.left = Node(node.l, node.mid) - if node.right is None: - node.right = Node(node.mid + 1, node.r) - if node.add != 0: - left, right = node.left, node.right - left.add = node.add - right.add = node.add - left.v = left.r - left.l + 1 - right.v = right.r - right.l + 1 - node.add = 0 - - -class CountIntervals: - def __init__(self): - self.tree = SegmentTree() - - def add(self, left, right): - self.tree.modify(left, right, 1) - - def count(self): - return self.tree.query(1, int(1e9)) - - -# Your CountIntervals object will be instantiated and called as such: -# obj = CountIntervals() -# obj.add(left, right) -# param_2 = obj.count() -``` - - - - - diff --git a/solution/2200-2299/2276.Count Integers in Intervals/Solution.py b/solution/2200-2299/2276.Count Integers in Intervals/Solution.py index 0871b73b87a2e..d21aac9131109 100644 --- a/solution/2200-2299/2276.Count Integers in Intervals/Solution.py +++ b/solution/2200-2299/2276.Count Integers in Intervals/Solution.py @@ -1,42 +1,80 @@ class Node: - def __init__(self): - self.tag = 0 - self.tot = 0 + __slots__ = ("left", "right", "l", "r", "mid", "v", "add") + + def __init__(self, l, r): self.left = None self.right = None + self.l = l + self.r = r + self.mid = (l + r) // 2 + self.v = 0 + self.add = 0 + + +class SegmentTree: + def __init__(self): + self.root = Node(1, int(1e9) + 1) - def update(self, l, r, a, b): - if self.tag == 1: + def modify(self, l, r, v, node=None): + if node is None: + node = self.root + if l > r: return - mid = (a + b) >> 1 - if l == a and r == b: - self.tag = 1 - self.tot = b - a + 1 + if node.l >= l and node.r <= r: + node.v = node.r - node.l + 1 + node.add = v return - if not self.left: - self.left = Node() - if not self.right: - self.right = Node() - if mid >= l: - self.left.update(l, min(mid, r), a, mid) - if mid + 1 <= r: - self.right.update(max(mid + 1, l), r, mid + 1, b) - self.tag = 0 - self.tot = self.left.tot + self.right.tot + self.pushdown(node) + if l <= node.mid: + self.modify(l, r, v, node.left) + if r > node.mid: + self.modify(l, r, v, node.right) + self.pushup(node) + + def query(self, l, r, node=None): + if node is None: + node = self.root + if l > r: + return 0 + if node.l >= l and node.r <= r: + return node.v + self.pushdown(node) + v = 0 + if l <= node.mid: + v += self.query(l, r, node.left) + if r > node.mid: + v += self.query(l, r, node.right) + return v + + def pushup(self, node): + node.v = node.left.v + node.right.v + + def pushdown(self, node): + if node.left is None: + node.left = Node(node.l, node.mid) + if node.right is None: + node.right = Node(node.mid + 1, node.r) + if node.add != 0: + left, right = node.left, node.right + left.add = node.add + right.add = node.add + left.v = left.r - left.l + 1 + right.v = right.r - right.l + 1 + node.add = 0 class CountIntervals: def __init__(self): - self.tree = Node() + self.tree = SegmentTree() - def add(self, left: int, right: int) -> None: - self.tree.update(left, right, 0, 1000000010) + def add(self, left, right): + self.tree.modify(left, right, 1) - def count(self) -> int: - return self.tree.tot + def count(self): + return self.tree.query(1, int(1e9)) # Your CountIntervals object will be instantiated and called as such: # obj = CountIntervals() -# obj.add(left,right) +# obj.add(left, right) # param_2 = obj.count() diff --git a/solution/2200-2299/2276.Count Integers in Intervals/Solution2.py b/solution/2200-2299/2276.Count Integers in Intervals/Solution2.py deleted file mode 100644 index d21aac9131109..0000000000000 --- a/solution/2200-2299/2276.Count Integers in Intervals/Solution2.py +++ /dev/null @@ -1,80 +0,0 @@ -class Node: - __slots__ = ("left", "right", "l", "r", "mid", "v", "add") - - def __init__(self, l, r): - self.left = None - self.right = None - self.l = l - self.r = r - self.mid = (l + r) // 2 - self.v = 0 - self.add = 0 - - -class SegmentTree: - def __init__(self): - self.root = Node(1, int(1e9) + 1) - - def modify(self, l, r, v, node=None): - if node is None: - node = self.root - if l > r: - return - if node.l >= l and node.r <= r: - node.v = node.r - node.l + 1 - node.add = v - return - self.pushdown(node) - if l <= node.mid: - self.modify(l, r, v, node.left) - if r > node.mid: - self.modify(l, r, v, node.right) - self.pushup(node) - - def query(self, l, r, node=None): - if node is None: - node = self.root - if l > r: - return 0 - if node.l >= l and node.r <= r: - return node.v - self.pushdown(node) - v = 0 - if l <= node.mid: - v += self.query(l, r, node.left) - if r > node.mid: - v += self.query(l, r, node.right) - return v - - def pushup(self, node): - node.v = node.left.v + node.right.v - - def pushdown(self, node): - if node.left is None: - node.left = Node(node.l, node.mid) - if node.right is None: - node.right = Node(node.mid + 1, node.r) - if node.add != 0: - left, right = node.left, node.right - left.add = node.add - right.add = node.add - left.v = left.r - left.l + 1 - right.v = right.r - right.l + 1 - node.add = 0 - - -class CountIntervals: - def __init__(self): - self.tree = SegmentTree() - - def add(self, left, right): - self.tree.modify(left, right, 1) - - def count(self): - return self.tree.query(1, int(1e9)) - - -# Your CountIntervals object will be instantiated and called as such: -# obj = CountIntervals() -# obj.add(left, right) -# param_2 = obj.count() diff --git a/solution/2200-2299/2282.Number of People That Can Be Seen in a Grid/README_EN.md b/solution/2200-2299/2282.Number of People That Can Be Seen in a Grid/README_EN.md index 9605ecd80704a..00fe6a0691470 100644 --- a/solution/2200-2299/2282.Number of People That Can Be Seen in a Grid/README_EN.md +++ b/solution/2200-2299/2282.Number of People That Can Be Seen in a Grid/README_EN.md @@ -74,7 +74,25 @@ tags: -### Solution 1 +### Solution 1: Monotonic Stack + +We observe that for the $i$-th person, the people he can see must have heights that are strictly monotonically increasing from left to right (or from top to bottom). + +Therefore, for each row, we can use a monotonic stack to find the number of people each person can see. + +Specifically, we can traverse the array in reverse order, using a stack $stk$ that is monotonically increasing from top to bottom to record the heights of the people we have traversed. + +For the $i$-th person, if the stack is not empty and the top element of the stack is less than $heights[i]$, we increment the number of people the $i$-th person can see, and then pop the top element of the stack, repeating this until the stack is empty or the top element of the stack is greater than or equal to $heights[i]$. If the stack is not empty at this point, it means the top element of the stack is greater than or equal to $heights[i]$, so we increment the number of people the $i$-th person can see by 1. Next, if the stack is not empty and the top element of the stack is equal to $heights[i]$, we pop the top element of the stack. Finally, we push $heights[i]$ onto the stack and continue to the next person. + +After processing this way, we can get the number of people each person can see for each row. + +Similarly, we can process each column to get the number of people each person can see for each column. Finally, we add the answers for each row and each column to get the final answer. + +The time complexity is $O(m \times n)$, and the space complexity is $O(\max(m, n))$. Where $m$ and $n$ are the number of rows and columns of the array $heights$, respectively. + +Similar problems: + +- [1944. Number of Visible People in a Queue](https://github.com/doocs/leetcode/blob/main/solution/1900-1999/1944.Number%20of%20Visible%20People%20in%20a%20Queue/README_EN.md) diff --git a/solution/2200-2299/2287.Rearrange Characters to Make Target String/README.md b/solution/2200-2299/2287.Rearrange Characters to Make Target String/README.md index 9eed4b624ac9e..d13824088073d 100644 --- a/solution/2200-2299/2287.Rearrange Characters to Make Target String/README.md +++ b/solution/2200-2299/2287.Rearrange Characters to Make Target String/README.md @@ -44,7 +44,7 @@ tags: 输入:s = "abcba", target = "abc" 输出:1 解释: -选取下标为 0 、1 和 2 的字符,可以形成 "abc" 的 1 个副本。 +选取下标为 0 、1 和 2 的字符,可以形成 "abc" 的 1 个副本。 可以形成最多 1 个 "abc" 的副本,所以返回 1 。 注意,尽管下标 3 和 4 分别有额外的 'a' 和 'b' ,但不能重用下标 2 处的 'c' ,所以无法形成 "abc" 的第 2 个副本。 @@ -81,9 +81,9 @@ tags: ### 方法一:计数 -我们统计字符串 `s` 和 `target` 中每个字符出现的次数,记为 `cnt1` 和 `cnt2`。对于 `target` 中的每个字符,我们计算 `cnt1` 中该字符出现的次数除以 `cnt2` 中该字符出现的次数,取最小值即可。 +我们统计字符串 $\textit{s}$ 和 $\textit{target}$ 中每个字符出现的次数,记为 $\textit{cnt1}$ 和 $\textit{cnt2}$。对于 $\textit{target}$ 中的每个字符,我们计算 $\textit{cnt1}$ 中该字符出现的次数除以 $\textit{cnt2}$ 中该字符出现的次数,取最小值即可。 -时间复杂度 $O(n + m)$,空间复杂度 $O(C)$。其中 $n$ 和 $m$ 分别是字符串 `s` 和 `target` 的长度。而 $C$ 是字符集的大小,本题中 $C=26$。 +时间复杂度 $O(n + m)$,空间复杂度 $O(|\Sigma|)$。其中 $n$ 和 $m$ 分别是字符串 $\textit{s}$ 和 $\textit{target}$ 的长度。而 $|\Sigma|$ 是字符集的大小,本题中 $|\Sigma|=26$。 diff --git a/solution/2200-2299/2287.Rearrange Characters to Make Target String/README_EN.md b/solution/2200-2299/2287.Rearrange Characters to Make Target String/README_EN.md index 0b63658125d88..9a7d8ca481065 100644 --- a/solution/2200-2299/2287.Rearrange Characters to Make Target String/README_EN.md +++ b/solution/2200-2299/2287.Rearrange Characters to Make Target String/README_EN.md @@ -76,7 +76,11 @@ We can make at most one copy of "aaaaa", so we return 1. -### Solution 1 +### Solution 1: Counting + +We count the occurrences of each character in the strings $\textit{s}$ and $\textit{target}$, denoted as $\textit{cnt1}$ and $\textit{cnt2}$. For each character in $\textit{target}$, we calculate the number of times it appears in $\textit{cnt1}$ divided by the number of times it appears in $\textit{cnt2}$, and take the minimum value. + +The time complexity is $O(n + m)$, and the space complexity is $O(|\Sigma|)$. Where $n$ and $m$ are the lengths of the strings $\textit{s}$ and $\textit{target}$, respectively. And $|\Sigma|$ is the size of the character set, which is 26 in this problem. diff --git a/solution/2200-2299/2290.Minimum Obstacle Removal to Reach Corner/README_EN.md b/solution/2200-2299/2290.Minimum Obstacle Removal to Reach Corner/README_EN.md index 3fe7d2e1ccced..cb8784b797cf5 100644 --- a/solution/2200-2299/2290.Minimum Obstacle Removal to Reach Corner/README_EN.md +++ b/solution/2200-2299/2290.Minimum Obstacle Removal to Reach Corner/README_EN.md @@ -71,7 +71,19 @@ Note that there may be other ways to remove 2 obstacles to create a path. -### Solution 1 +### Solution 1: Double-Ended Queue BFS + +This problem is essentially a shortest path model, but we need to find the minimum number of obstacles to remove. + +In an undirected graph with edge weights of only $0$ and $1$, we can use a double-ended queue to perform BFS. The principle is that if the weight of the current point that can be expanded is $0$, it is added to the front of the queue; if the weight is $1$, it is added to the back of the queue. + +> If the weight of an edge is $0$, then the newly expanded node has the same weight as the current front node, and it can obviously be used as the starting point for the next expansion. + +The time complexity is $O(m \times n)$, and the space complexity is $O(m \times n)$. Where $m$ and $n$ are the number of rows and columns of the grid, respectively. + +Similar problems: + +- [1368. Minimum Cost to Make at Least One Valid Path in a Grid](https://github.com/doocs/leetcode/blob/main/solution/1300-1399/1368.Minimum%20Cost%20to%20Make%20at%20Least%20One%20Valid%20Path%20in%20a%20Grid/README_EN.md) diff --git a/solution/2200-2299/2291.Maximum Profit From Trading Stocks/README.md b/solution/2200-2299/2291.Maximum Profit From Trading Stocks/README.md index 928d477f9b72a..297ccf32c5a17 100644 --- a/solution/2200-2299/2291.Maximum Profit From Trading Stocks/README.md +++ b/solution/2200-2299/2291.Maximum Profit From Trading Stocks/README.md @@ -66,18 +66,16 @@ tags: ### 方法一:动态规划 -我们定义 $f[i][j]$ 表示前 $i$ 支股票,预算为 $j$ 时的最大收益。那么答案就是 $f[n][budget]$。 +我们定义 $f[i][j]$ 表示前 $i$ 支股票,预算为 $j$ 时的最大收益。那么答案就是 $f[n][\textit{budget}]$。 对于第 $i$ 支股票,我们有两种选择: - 不购买,那么 $f[i][j] = f[i - 1][j]$; -- 购买,那么 $f[i][j] = f[i - 1][j - present[i]] + future[i] - present[i]$。 +- 购买,那么 $f[i][j] = f[i - 1][j - \textit{present}[i]] + \textit{future}[i] - \textit{present}[i]$。 -最后返回 $f[n][budget]$ 即可。 +最后返回 $f[n][\textit{budget}]$ 即可。 -时间复杂度 $O(n \times budget)$,空间复杂度 $O(n \times budget)$。其中 $n$ 为数组长度。 - -我们可以发现,对于每一行,我们只需要用到上一行的值,因此可以将空间复杂度优化到 $O(budget)$。 +时间复杂度 $O(n \times \textit{budget})$,空间复杂度 $O(n \times \textit{budget})$。其中 $n$ 为数组长度。 @@ -180,7 +178,9 @@ function maximumProfit(present: number[], future: number[], budget: number): num -### 方法二 +### 方法二:动态规划(空间优化) + +我们可以发现,对于每一行,我们只需要用到上一行的值,因此可以将空间复杂度优化到 $O(\text{budget})$。 diff --git a/solution/2200-2299/2291.Maximum Profit From Trading Stocks/README_EN.md b/solution/2200-2299/2291.Maximum Profit From Trading Stocks/README_EN.md index 10abc56c4cf7b..ab4d39a3e9539 100644 --- a/solution/2200-2299/2291.Maximum Profit From Trading Stocks/README_EN.md +++ b/solution/2200-2299/2291.Maximum Profit From Trading Stocks/README_EN.md @@ -70,7 +70,18 @@ It can be shown that the maximum profit you can make is 0. -### Solution 1 +### Solution 1: Dynamic Programming + +We define $f[i][j]$ to represent the maximum profit when considering the first $i$ stocks with a budget of $j$. The answer is $f[n][\textit{budget}]$. + +For the $i$-th stock, we have two choices: + +- Do not buy it, then $f[i][j] = f[i - 1][j]$; +- Buy it, then $f[i][j] = f[i - 1][j - \textit{present}[i]] + \textit{future}[i] - \textit{present}[i]$. + +Finally, return $f[n][\textit{budget}]$. + +The time complexity is $O(n \times \textit{budget})$, and the space complexity is $O(n \times \textit{budget})$. Where $n$ is the length of the array. @@ -173,7 +184,9 @@ function maximumProfit(present: number[], future: number[], budget: number): num -### Solution 2 +### Solution 2: Dynamic Programming (Space Optimization) + +We can observe that for each row, we only need the values from the previous row, so we can optimize the space complexity to $O(\text{budget})$. diff --git a/solution/2300-2399/2311.Longest Binary Subsequence Less Than or Equal to K/README.md b/solution/2300-2399/2311.Longest Binary Subsequence Less Than or Equal to K/README.md index c680901ec6715..6e48a84065363 100644 --- a/solution/2300-2399/2311.Longest Binary Subsequence Less Than or Equal to K/README.md +++ b/solution/2300-2399/2311.Longest Binary Subsequence Less Than or Equal to K/README.md @@ -72,7 +72,7 @@ tags: 最长二进制子序列必然包含原字符串中所有的 $0$,在此基础上,我们从右到左遍历 $s$,若遇到 $1$,判断子序列能否添加 $1$,使得子序列对应的二进制数字 $v \leq k$。 -时间复杂度 $O(n)$,空间复杂度 $O(1)$。其中 $n$ 为字符串 $s$ 的长度。 +时间复杂度 $O(n)$,其中 $n$ 为字符串 $s$ 的长度。空间复杂度 $O(1)$。 diff --git a/solution/2300-2399/2311.Longest Binary Subsequence Less Than or Equal to K/README_EN.md b/solution/2300-2399/2311.Longest Binary Subsequence Less Than or Equal to K/README_EN.md index 653789687bf26..9b28e43961e63 100644 --- a/solution/2300-2399/2311.Longest Binary Subsequence Less Than or Equal to K/README_EN.md +++ b/solution/2300-2399/2311.Longest Binary Subsequence Less Than or Equal to K/README_EN.md @@ -68,7 +68,11 @@ The length of this subsequence is 6, so 6 is returned. -### Solution 1 +### Solution 1: Greedy + +The longest binary subsequence must include all the $0$s in the original string. On this basis, we traverse $s$ from right to left. If we encounter a $1$, we check if adding this $1$ to the subsequence keeps the binary number $v \leq k$. + +The time complexity is $O(n)$, where $n$ is the length of the string $s$. The space complexity is $O(1)$. diff --git a/solution/2300-2399/2313.Minimum Flips in Binary Tree to Get Result/README_EN.md b/solution/2300-2399/2313.Minimum Flips in Binary Tree to Get Result/README_EN.md index 3abe488471b50..074c43f4ca42a 100644 --- a/solution/2300-2399/2313.Minimum Flips in Binary Tree to Get Result/README_EN.md +++ b/solution/2300-2399/2313.Minimum Flips in Binary Tree to Get Result/README_EN.md @@ -81,7 +81,23 @@ The root of the tree already evaluates to false, so 0 nodes have to be flipped. -### Solution 1 +### Solution 1: Tree DP + Case Analysis + +We define a function $dfs(root)$, which returns an array of length 2. The first element represents the minimum number of flips needed to change the value of the $root$ node to `false`, and the second element represents the minimum number of flips needed to change the value of the $root$ node to `true`. The answer is $dfs(root)[result]$. + +The implementation of the function $dfs(root)$ is as follows: + +If $root$ is null, return $[+\infty, +\infty]$. + +Otherwise, let $x$ be the value of $root$, $l$ be the return value of the left subtree, and $r$ be the return value of the right subtree. Then we discuss the following cases: + +- If $x \in \{0, 1\}$, return $[x, x \oplus 1]$. +- If $x = 2$, which means the boolean operator is `OR`, to make the value of $root$ `false`, we need to make both the left and right subtrees `false`. Therefore, the first element of the return value is $l[0] + r[0]$. To make the value of $root$ `true`, we need at least one of the left or right subtrees to be `true`. Therefore, the second element of the return value is $\min(l[0] + r[1], l[1] + r[0], l[1] + r[1])$. +- If $x = 3$, which means the boolean operator is `AND`, to make the value of $root$ `false`, we need at least one of the left or right subtrees to be `false`. Therefore, the first element of the return value is $\min(l[0] + r[0], l[0] + r[1], l[1] + r[0])$. To make the value of $root$ `true`, we need both the left and right subtrees to be `true`. Therefore, the second element of the return value is $l[1] + r[1]$. +- If $x = 4$, which means the boolean operator is `XOR`, to make the value of $root$ `false`, we need both the left and right subtrees to be either `false` or `true`. Therefore, the first element of the return value is $\min(l[0] + r[0], l[1] + r[1])$. To make the value of $root$ `true`, we need the left and right subtrees to be different. Therefore, the second element of the return value is $\min(l[0] + r[1], l[1] + r[0])$. +- If $x = 5$, which means the boolean operator is `NOT`, to make the value of $root$ `false`, we need at least one of the left or right subtrees to be `true`. Therefore, the first element of the return value is $\min(l[1], r[1])$. To make the value of $root$ `true`, we need at least one of the left or right subtrees to be `false`. Therefore, the second element of the return value is $\min(l[0], r[0])$. + +The time complexity is $O(n)$, and the space complexity is $O(n)$. Where $n$ is the number of nodes in the binary tree. diff --git a/solution/2300-2399/2315.Count Asterisks/README.md b/solution/2300-2399/2315.Count Asterisks/README.md index 209529a6d4bb2..d439f443a0dc6 100644 --- a/solution/2300-2399/2315.Count Asterisks/README.md +++ b/solution/2300-2399/2315.Count Asterisks/README.md @@ -66,13 +66,13 @@ tags: ### 方法一:模拟 -我们定义一个整型变量 $ok$,表示遇到 `*` 时是否能计数,初始时 $ok=1$,表示可以计数。 +我们定义一个整型变量 $\textit{ok}$,表示遇到 `*` 时是否能计数,初始时 $\textit{ok}=1$,表示可以计数。 -遍历字符串 $s$,如果遇到 `*`,则根据 $ok$ 的值决定是否计数,如果遇到 `|`,则 $ok$ 的值取反。 +遍历字符串 $s$,如果遇到 `*`,则根据 $\textit{ok}$ 的值决定是否计数,如果遇到 `|`,则 $\textit{ok}$ 的值取反。 最后返回计数的结果。 -时间复杂度 $O(n)$,空间复杂度 $O(1)$。其中 $n$ 为字符串 $s$ 的长度。 +时间复杂度 $O(n)$,其中 $n$ 为字符串 $s$ 的长度。空间复杂度 $O(1)$。 diff --git a/solution/2300-2399/2315.Count Asterisks/README_EN.md b/solution/2300-2399/2315.Count Asterisks/README_EN.md index 6d183486e43d9..d17a7c837853e 100644 --- a/solution/2300-2399/2315.Count Asterisks/README_EN.md +++ b/solution/2300-2399/2315.Count Asterisks/README_EN.md @@ -65,7 +65,15 @@ There are 2 asterisks considered. Therefore, we return 2. -### Solution 1 +### Solution 1: Simulation + +We define an integer variable $\textit{ok}$ to indicate whether we can count when encountering `*`. Initially, $\textit{ok}=1$, meaning we can count. + +Traverse the string $s$. If we encounter `*`, we decide whether to count based on the value of $\textit{ok}$. If we encounter `|`, we toggle the value of $\textit{ok}$. + +Finally, return the count result. + +The time complexity is $O(n)$, where $n$ is the length of the string $s$. The space complexity is $O(1)$. diff --git a/solution/2300-2399/2317.Maximum XOR After Operations/README.md b/solution/2300-2399/2317.Maximum XOR After Operations/README.md index 96109e454a02e..83b782f2763e1 100644 --- a/solution/2300-2399/2317.Maximum XOR After Operations/README.md +++ b/solution/2300-2399/2317.Maximum XOR After Operations/README.md @@ -62,11 +62,11 @@ tags: ### 方法一:位运算 -在一次操作中,我们可以把 `nums[i]` 更新为 `nums[i] AND (nums[i] XOR x)`。由于 $x$ 是任意非负整数,因此 $nums[i] \oplus x$ 的结果是一个任意值,再与 `nums[i]` 逐位与运算,可以把 `nums[i]` 的二进制表示中的若干位 $1$ 变为 $0$。 +在一次操作中,我们可以把 $\textit{nums}[i]$ 更新为 $\textit{nums}[i] \text{ AND } (\textit{nums}[i] \text{ XOR } x)$。由于 $x$ 是任意非负整数,因此 $\textit{nums}[i] \oplus x$ 的结果是一个任意值,再与 $\textit{nums}[i]$ 逐位与运算,可以把 $\textit{nums}[i]$ 的二进制表示中的若干位 $1$ 变为 $0$。 -而题目中要获取的是 `nums` 所有元素的最大逐位异或和,对于一个二进制位,只要在 `nums` 中存在一个元素对应的二进制位为 $1$,那么这个二进制位对于最大逐位异或和的贡献就是 $1$。因此答案就是 `nums` 中所有元素的逐位或运算的结果。 +而题目中要获取的是 $\textit{nums}$ 所有元素的最大逐位异或和,对于一个二进制位,只要在 $\textit{nums}$ 中存在一个元素对应的二进制位为 $1$,那么这个二进制位对于最大逐位异或和的贡献就是 $1$。因此答案就是 $\textit{nums}$ 中所有元素的逐位或运算的结果。 -时间复杂度 $O(n)$,空间复杂度 $O(1)$。其中 $n$ 为 `nums` 的长度。 +时间复杂度 $O(n)$,其中 $n$ 为 $\textit{nums}$ 的长度。空间复杂度 $O(1)$。 diff --git a/solution/2300-2399/2317.Maximum XOR After Operations/README_EN.md b/solution/2300-2399/2317.Maximum XOR After Operations/README_EN.md index f9b949b91ea15..cfe1299283c21 100644 --- a/solution/2300-2399/2317.Maximum XOR After Operations/README_EN.md +++ b/solution/2300-2399/2317.Maximum XOR After Operations/README_EN.md @@ -60,7 +60,13 @@ It can be shown that 11 is the maximum possible bitwise XOR. -### Solution 1 +### Solution 1: Bit Manipulation + +In one operation, we can update $\textit{nums}[i]$ to $\textit{nums}[i] \text{ AND } (\textit{nums}[i] \text{ XOR } x)$. Since $x$ is any non-negative integer, the result of $\textit{nums}[i] \oplus x$ can be any value. By performing a bitwise AND operation with $\textit{nums}[i]$, we can change some of the $1$ bits in the binary representation of $\textit{nums}[i]$ to $0$. + +The problem requires us to find the maximum bitwise XOR sum of all elements in $\textit{nums}$. For a binary bit, as long as there is an element in $\textit{nums}$ with the corresponding binary bit set to $1$, the contribution of this binary bit to the maximum bitwise XOR sum is $1$. Therefore, the answer is the result of the bitwise OR operation of all elements in $\textit{nums}$. + +The time complexity is $O(n)$, where $n$ is the length of $\textit{nums}$. The space complexity is $O(1)$. diff --git a/solution/2300-2399/2319.Check if Matrix Is X-Matrix/README.md b/solution/2300-2399/2319.Check if Matrix Is X-Matrix/README.md index 18ff945b08dfb..29f5c2431c344 100644 --- a/solution/2300-2399/2319.Check if Matrix Is X-Matrix/README.md +++ b/solution/2300-2399/2319.Check if Matrix Is X-Matrix/README.md @@ -66,9 +66,9 @@ X 矩阵应该满足:绿色元素(对角线上)都不是 0 ,红色元素 ### 方法一:模拟 -遍历矩阵,对于每个元素,判断其是否满足 $X$ 矩阵的条件。若不满足,直接返回 `false`;若遍历完所有元素都满足,返回 `true`。 +我们可以直接遍历矩阵,对于每个元素,判断其是否满足 $X$ 矩阵的条件。若不满足,直接返回 $\textit{false}$;若遍历完所有元素都满足,返回 $\textit{true}$。 -时间复杂度 $O(n^2)$,空间复杂度 $O(1)$。其中 $n$ 为矩阵的行数或列数。 +时间复杂度 $O(n^2)$,其中 $n$ 为矩阵的行数或列数。空间复杂度 $O(1)$。 diff --git a/solution/2300-2399/2319.Check if Matrix Is X-Matrix/README_EN.md b/solution/2300-2399/2319.Check if Matrix Is X-Matrix/README_EN.md index b098396ee1bcc..358d98c06a7b8 100644 --- a/solution/2300-2399/2319.Check if Matrix Is X-Matrix/README_EN.md +++ b/solution/2300-2399/2319.Check if Matrix Is X-Matrix/README_EN.md @@ -34,7 +34,7 @@ tags:
Input: grid = [[2,0,0,1],[0,3,1,0],[0,5,2,0],[4,0,0,2]] Output: true -Explanation: Refer to the diagram above. +Explanation: Refer to the diagram above. An X-Matrix should have the green elements (diagonals) be non-zero and the red elements be 0. Thus, grid is an X-Matrix.@@ -64,7 +64,11 @@ Thus, grid is not an X-Matrix. -### Solution 1 +### Solution 1: Simulation + +We can directly traverse the matrix and check if each element satisfies the conditions of an $X$ matrix. If any element does not satisfy the conditions, return $\textit{false}$ immediately. If all elements satisfy the conditions after traversal, return $\textit{true}$. + +The time complexity is $O(n^2)$, where $n$ is the number of rows or columns of the matrix. The space complexity is $O(1)$. diff --git a/solution/2300-2399/2320.Count Number of Ways to Place Houses/README_EN.md b/solution/2300-2399/2320.Count Number of Ways to Place Houses/README_EN.md index 8406046e8481b..c373fda18fe9c 100644 --- a/solution/2300-2399/2320.Count Number of Ways to Place Houses/README_EN.md +++ b/solution/2300-2399/2320.Count Number of Ways to Place Houses/README_EN.md @@ -30,7 +30,7 @@ tags:
Input: n = 1 Output: 4 -Explanation: +Explanation: Possible arrangements: 1. All plots are empty. 2. A house is placed on one side of the street. @@ -59,7 +59,20 @@ Possible arrangements: -### Solution 1 +### Solution 1: Dynamic Programming + +Since the placement of houses on both sides of the street does not affect each other, we can consider the placement on one side only, and then square the number of ways for one side to get the final result modulo. + +We define $f[i]$ to represent the number of ways to place houses on the first $i+1$ plots, with the last plot having a house. We define $g[i]$ to represent the number of ways to place houses on the first $i+1$ plots, with the last plot not having a house. Initially, $f[0] = g[0] = 1$. + +When placing the $(i+1)$-th plot, there are two cases: + +- If the $(i+1)$-th plot has a house, then the $i$-th plot must not have a house, so the number of ways is $f[i] = g[i-1]$; +- If the $(i+1)$-th plot does not have a house, then the $i$-th plot can either have a house or not, so the number of ways is $g[i] = f[i-1] + g[i-1]$. + +Finally, we square $f[n-1] + g[n-1]$ modulo to get the answer. + +The time complexity is $O(n)$, and the space complexity is $O(n)$. Where $n$ is the length of the street.