Skip to content

feat: update solutions to lc problems #3451

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ tags:

我们注意到,对于所有能覆盖某个左端点的水龙头,选择能覆盖最远右端点的那个水龙头是最优的。

因此,我们可以先预处理数组 $ranges$,对于第 $i$ 个水龙头,它能覆盖的左端点 $l = max(0, i - ranges[i])$,右端点 $r = i + ranges[i]$,我们算出所有能覆盖左端点 $l$ 的水龙头中,右端点最大的那个位置,记录在数组 $last[i]$ 中。
因此,我们可以先预处理数组 $ranges$,对于第 $i$ 个水龙头,它能覆盖的左端点 $l = \max(0, i - ranges[i])$,右端点 $r = i + ranges[i]$,我们算出所有能覆盖左端点 $l$ 的水龙头中,右端点最大的那个位置,记录在数组 $last[i]$ 中。

然后我们定义以下三个变量,其中:

- 变量 $ans$ 表示最终答案,即最少水龙头数目;
- 变量 $mx$ 表示当前能覆盖的最远右端点;
- 变量 $pre$ 表示上一个水龙头覆盖的最远右端点。

我们在 $[0,...n-1]$ 的范围内遍历所有位置,对于当前位置 $i$,我们用 $last[i]$ 更新 $mx$,即 $mx = max(mx, last[i])$。
我们在 $[0,...n-1]$ 的范围内遍历所有位置,对于当前位置 $i$,我们用 $last[i]$ 更新 $mx$,即 $mx = \max(mx, last[i])$。

- 如果 $mx \leq i$,说明无法覆盖下一个位置,返回 $-1$。
- 如果 $pre = i$,说明需要使用一个新的子区间,因此我们将 $ans$ 加 $1$,并且更新 $pre = mx$。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,32 @@ Opening Only the second tap will water the whole garden [0,5]

<!-- solution:start -->

### Solution 1
### Solution 1: Greedy

We note that for all taps that can cover a certain left endpoint, choosing the tap that can cover the farthest right endpoint is optimal.

Therefore, we can preprocess the array $ranges$. For the $i$-th tap, it can cover the left endpoint $l = \max(0, i - ranges[i])$ and the right endpoint $r = i + ranges[i]$. We calculate the position of the tap that can cover the left endpoint $l$ with the farthest right endpoint and record it in the array $last[i]$.

Then we define the following three variables:

- Variable $ans$ represents the final answer, i.e., the minimum number of taps;
- Variable $mx$ represents the farthest right endpoint that can currently be covered;
- Variable $pre$ represents the farthest right endpoint covered by the previous tap.

We traverse all positions in the range $[0, \ldots, n-1]$. For the current position $i$, we use $last[i]$ to update $mx$, i.e., $mx = \max(mx, last[i])$.

- If $mx \leq i$, it means the next position cannot be covered, so we return $-1$.
- If $pre = i$, it means a new subinterval needs to be used, so we increment $ans$ by $1$ and update $pre = mx$.

After the traversal, we return $ans$.

The time complexity is $O(n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the garden.

Similar problems:

- [45. Jump Game II](https://github.com/doocs/leetcode/blob/main/solution/0000-0099/0045.Jump%20Game%20II/README.md)
- [55. Jump Game](https://github.com/doocs/leetcode/blob/main/solution/0000-0099/0055.Jump%20Game/README.md)
- [1024. Video Stitching](https://github.com/doocs/leetcode/blob/main/solution/1000-1099/1024.Video%20Stitching/README.md)

<!-- tabs:start -->

Expand Down
8 changes: 7 additions & 1 deletion solution/1300-1399/1328.Break a Palindrome/README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,13 @@ Of all the ways, &quot;aaccba&quot; is the lexicographically smallest.

<!-- solution:start -->

### Solution 1
### Solution 1: Greedy

First, we check if the length of the string is $1$. If it is, we directly return an empty string.

Otherwise, we traverse the first half of the string from left to right, find the first character that is not `'a'`, and change it to `'a'`. If no such character exists, we change the last character to `'b'`.

The time complexity is $O(n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the string.

<!-- tabs:start -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,52 @@ tags:

<!-- solution:start -->

### Solution 1
### Solution 1: Classification Discussion + Enumeration

According to the problem description, we need to find the maximum value of the array $\sum_{i=0}^{n-2} |a_i - a_{i+1}|$ when reversing a subarray once.

Next, we discuss the following cases:

1. Do not reverse the subarray.
2. Reverse the subarray, and the subarray "includes" the first element.
3. Reverse the subarray, and the subarray "includes" the last element.
4. Reverse the subarray, and the subarray "does not include" the first and last elements.

Let $s$ be the array value when the subarray is not reversed, then $s = \sum_{i=0}^{n-2} |a_i - a_{i+1}|$. We can initialize the answer $ans$ to $s$.

If we reverse the subarray and the subarray includes the first element, we can enumerate the last element $a_i$ of the reversed subarray, where $0 \leq i < n-1$. In this case, $ans = \max(ans, s + |a_0 - a_{i+1}| - |a_i - a_{i+1}|)$.

<p><img alt="" src="https://fastly.jsdelivr.net/gh/doocs/leetcode@main/solution/1300-1399/1330.Reverse%20Subarray%20To%20Maximize%20Array%20Value/images/1-drawio.png" /></p>

Similarly, if we reverse the subarray and the subarray includes the last element, we can enumerate the first element $a_{i+1}$ of the reversed subarray, where $0 \leq i < n-1$. In this case, $ans = \max(ans, s + |a_{n-1} - a_i| - |a_i - a_{i+1}|)$.

<p><img alt="" src="https://fastly.jsdelivr.net/gh/doocs/leetcode@main/solution/1300-1399/1330.Reverse%20Subarray%20To%20Maximize%20Array%20Value/images/2-drawio.png" /></p>

If we reverse the subarray and the subarray does not include the first and last elements, we consider any two adjacent elements in the array as a point pair $(x, y)$. Let the first element of the reversed subarray be $y_1$, and its left adjacent element be $x_1$; let the last element of the reversed subarray be $x_2$, and its right adjacent element be $y_2$.

<p><img alt="" src="https://fastly.jsdelivr.net/gh/doocs/leetcode@main/solution/1300-1399/1330.Reverse%20Subarray%20To%20Maximize%20Array%20Value/images/3-drawio.png" /></p>

At this time, compared to not reversing the subarray, the change in the array value is $|x_1 - x_2| + |y_1 - y_2| - |x_1 - y_1| - |x_2 - y_2|$, where the first two terms can be expressed as:

$$
\left | x_1 - x_2 \right | + \left | y_1 - y_2 \right | = \max \begin{cases} (x_1 + y_1) - (x_2 + y_2) \\ (x_1 - y_1) - (x_2 - y_2) \\ (-x_1 + y_1) - (-x_2 + y_2) \\ (-x_1 - y_1) - (-x_2 - y_2) \end{cases}
$$

Then the change in the array value is:

$$
\left | x_1 - x_2 \right | + \left | y_1 - y_2 \right | - \left | x_1 - y_1 \right | - \left | x_2 - y_2 \right | = \max \begin{cases} (x_1 + y_1) - \left |x_1 - y_1 \right | - \left ( (x_2 + y_2) + \left |x_2 - y_2 \right | \right ) \\ (x_1 - y_1) - \left |x_1 - y_1 \right | - \left ( (x_2 - y_2) + \left |x_2 - y_2 \right | \right ) \\ (-x_1 + y_1) - \left |x_1 - y_1 \right | - \left ( (-x_2 + y_2) + \left |x_2 - y_2 \right | \right ) \\ (-x_1 - y_1) - \left |x_1 - y_1 \right | - \left ( (-x_2 - y_2) + \left |x_2 - y_2 \right | \right ) \end{cases}
$$

Therefore, we only need to find the maximum value $mx$ of $k_1 \times x + k_2 \times y$, where $k_1, k_2 \in \{-1, 1\}$, and the corresponding minimum value $mi$ of $|x - y|$. Then the maximum change in the array value is $mx - mi$. The answer is $ans = \max(ans, s + \max(0, mx - mi))$.

In the code implementation, we define an array of length 5, $dirs=[1, -1, -1, 1, 1]$. Each time we take two adjacent elements of the array as the values of $k_1$ and $k_2$, which can cover all cases of $k_1, k_2 \in \{-1, 1\}$.

The time complexity is $O(n)$, where $n$ is the length of the array $nums$. The space complexity is $O(1)$.

Similar problems:

- [1131. Maximum of Absolute Value Expression](https://github.com/doocs/leetcode/blob/main/solution/1100-1199/1131.Maximum%20of%20Absolute%20Value%20Expression/README_EN.md)

<!-- tabs:start -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,13 @@ tags:

<!-- solution:start -->

### Solution 1
### Solution 1: Discretization

First, we copy an array $t$, then sort and deduplicate it to obtain an array of length $m$ that is strictly monotonically increasing.

Next, we traverse the original array $arr$. For each element $x$ in the array, we use binary search to find the position of $x$ in $t$. The position plus one is the rank of $x$.

The time complexity is $O(n \times \log n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array $arr$.

<!-- tabs:start -->

Expand Down
38 changes: 18 additions & 20 deletions solution/1400-1499/1409.Queries on a Permutation With Key/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ tags:

<pre>
<strong>输入:</strong>queries = [3,1,2,1], m = 5
<strong>输出:</strong>[2,1,2,1]
<strong>输出:</strong>[2,1,2,1]
<strong>解释:处理</strong> queries 的过程如下:
对于 i=0: queries[i]=3, P=[1,2,3,4,5], 3 在 P 中的位置是 <strong>2</strong>,然后我们把 3 移动到 P 的开头,得到 P=[3,1,2,4,5] 。
对于 i=1: queries[i]=1, P=[3,1,2,4,5], 1 在 P 中的位置是 <strong>1</strong>,然后我们把 1 移动到 P 的开头,得到 P=[1,3,2,4,5] 。
对于 i=1: queries[i]=1, P=[3,1,2,4,5], 1 在 P 中的位置是 <strong>1</strong>,然后我们把 1 移动到 P 的开头,得到 P=[1,3,2,4,5] 。
对于 i=2: queries[i]=2, P=[1,3,2,4,5], 2 在 P 中的位置是 <strong>2</strong>,然后我们把 2 移动到 P 的开头,得到 P=[2,1,3,4,5] 。
对于 i=3: queries[i]=1, P=[2,1,3,4,5], 1 在 P 中的位置是 <strong>1</strong>,然后我们把 1 移动到 P 的开头,得到 P=[1,2,3,4,5] 。
因此,包含结果的数组为 [2,1,2,1] 。
对于 i=3: queries[i]=1, P=[2,1,3,4,5], 1 在 P 中的位置是 <strong>1</strong>,然后我们把 1 移动到 P 的开头,得到 P=[1,2,3,4,5] 。
因此,包含结果的数组为 [2,1,2,1] 。
</pre>

<p><strong>示例 2:</strong></p>
Expand Down Expand Up @@ -78,21 +78,6 @@ tags:

题目数据规模不大,可以直接模拟。

**方法一:树状数组**

树状数组,也称作“二叉索引树”(Binary Indexed Tree)或 Fenwick 树。 它可以高效地实现如下两个操作:

1. **单点更新** `update(x, delta)`: 把序列 x 位置的数加上一个值 delta;
1. **前缀和查询** `query(x)`:查询序列 `[1,...x]` 区间的区间和,即位置 x 的前缀和。

这两个操作的时间复杂度均为 $O(\log n)$。

树状数组最基本的功能就是求比某点 x 小的点的个数(这里的比较是抽象的概念,可以是数的大小、坐标的大小、质量的大小等等)。

比如给定数组 `a[5] = {2, 5, 3, 4, 1}`,求 `b[i] = 位置 i 左边小于等于 a[i] 的数的个数`。对于此例,`b[5] = {0, 1, 1, 2, 0}`。

解决方案是直接遍历数组,每个位置先求出 `query(a[i])`,然后再修改树状数组 `update(a[i], 1)` 即可。当数的范围比较大时,需要进行离散化,即先进行去重并排序,然后对每个数字进行编号。

<!-- tabs:start -->

#### Python3
Expand Down Expand Up @@ -189,7 +174,20 @@ func processQueries(queries []int, m int) []int {

<!-- solution:start -->

### 方法二
### 方法二:树状数组

树状数组,也称作“二叉索引树”(Binary Indexed Tree)或 Fenwick 树。 它可以高效地实现如下两个操作:

1. **单点更新** `update(x, delta)`: 把序列 x 位置的数加上一个值 delta;
1. **前缀和查询** `query(x)`:查询序列 `[1,...x]` 区间的区间和,即位置 x 的前缀和。

这两个操作的时间复杂度均为 $O(\log n)$。

树状数组最基本的功能就是求比某点 x 小的点的个数(这里的比较是抽象的概念,可以是数的大小、坐标的大小、质量的大小等等)。

比如给定数组 `a[5] = {2, 5, 3, 4, 1}`,求 `b[i] = 位置 i 左边小于等于 a[i] 的数的个数`。对于此例,`b[5] = {0, 1, 1, 2, 0}`。

解决方案是直接遍历数组,每个位置先求出 `query(a[i])`,然后再修改树状数组 `update(a[i], 1)` 即可。当数的范围比较大时,需要进行离散化,即先进行去重并排序,然后对每个数字进行编号。

<!-- tabs:start -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ tags:

<pre>
<strong>Input:</strong> queries = [3,1,2,1], m = 5
<strong>Output:</strong> [2,1,2,1]
<strong>Explanation:</strong> The queries are processed as follow:
For i=0: queries[i]=3, P=[1,2,3,4,5], position of 3 in P is <strong>2</strong>, then we move 3 to the beginning of P resulting in P=[3,1,2,4,5].
For i=1: queries[i]=1, P=[3,1,2,4,5], position of 1 in P is <strong>1</strong>, then we move 1 to the beginning of P resulting in P=[1,3,2,4,5].
For i=2: queries[i]=2, P=[1,3,2,4,5], position of 2 in P is <strong>2</strong>, then we move 2 to the beginning of P resulting in P=[2,1,3,4,5].
For i=3: queries[i]=1, P=[2,1,3,4,5], position of 1 in P is <strong>1</strong>, then we move 1 to the beginning of P resulting in P=[1,2,3,4,5].
Therefore, the array containing the result is [2,1,2,1].
<strong>Output:</strong> [2,1,2,1]
<strong>Explanation:</strong> The queries are processed as follow:
For i=0: queries[i]=3, P=[1,2,3,4,5], position of 3 in P is <strong>2</strong>, then we move 3 to the beginning of P resulting in P=[3,1,2,4,5].
For i=1: queries[i]=1, P=[3,1,2,4,5], position of 1 in P is <strong>1</strong>, then we move 1 to the beginning of P resulting in P=[1,3,2,4,5].
For i=2: queries[i]=2, P=[1,3,2,4,5], position of 2 in P is <strong>2</strong>, then we move 2 to the beginning of P resulting in P=[2,1,3,4,5].
For i=3: queries[i]=1, P=[2,1,3,4,5], position of 1 in P is <strong>1</strong>, then we move 1 to the beginning of P resulting in P=[1,2,3,4,5].
Therefore, the array containing the result is [2,1,2,1].
</pre>

<p><strong class="example">Example 2:</strong></p>
Expand Down Expand Up @@ -72,7 +72,9 @@ Therefore, the array containing the result is [2,1,2,1].

<!-- solution:start -->

### Solution 1
### Solution 1: Simulation

The problem's data scale is not large, so we can directly simulate it.

<!-- tabs:start -->

Expand Down Expand Up @@ -170,7 +172,20 @@ func processQueries(queries []int, m int) []int {

<!-- solution:start -->

### Solution 2
### Solution 2: Binary Indexed Tree

The Binary Indexed Tree (BIT), also known as the Fenwick Tree, efficiently supports the following two operations:

1. **Point Update** `update(x, delta)`: Adds a value `delta` to the element at position `x` in the sequence.
2. **Prefix Sum Query** `query(x)`: Queries the sum of the sequence over the interval `[1,...,x]`, i.e., the prefix sum at position `x`.

Both operations have a time complexity of $O(\log n)$.

The fundamental functionality of the Binary Indexed Tree is to count the number of elements smaller than a given element `x`. This comparison is abstract and can refer to size, coordinate, mass, etc.

For example, given the array `a[5] = {2, 5, 3, 4, 1}`, the task is to compute `b[i] = the number of elements to the left of position i that are less than or equal to a[i]`. For this example, `b[5] = {0, 1, 1, 2, 0}`.

The solution is to traverse the array, first calculating `query(a[i])` for each position, and then updating the Binary Indexed Tree with `update(a[i], 1)`. When the range of numbers is large, discretization is necessary, which involves removing duplicates, sorting, and then assigning an index to each number.

<!-- tabs:start -->

Expand Down
Loading