Skip to content
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ package-lock.json
!.cache/plugin/git-committers/
!.cache/plugin/git-committers/page-authors.json

# Claude
.claude/settings.local.json

# pnpm
pnpm-lock.yaml
.pnpm-store/
Expand Down
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,6 @@
- [找到最小生成树里的关键边和伪关键边](/solution/1400-1499/1489.Find%20Critical%20and%20Pseudo-Critical%20Edges%20in%20Minimum%20Spanning%20Tree/README.md) - `最小生成树`、`Kruskal 算法`、`并查集`
- [判断二分图](/solution/0700-0799/0785.Is%20Graph%20Bipartite/README.md) - `染色法判定二分图`、`并查集`

<!-- 待补充
### 7. 数学知识
-->

## 加入我们

刷编程题的最大好处就是可以锻炼解决问题的思维能力。相信我,「如何去思考」​ 本身也是一项需要不断学习和练习的技能。非常感谢前微软工程师、现蚂蚁金服技术专家 [@kfstorm](https://github.com/kfstorm) 贡献了本项目的所有 [C# 题解](https://github.com/doocs/leetcode/pull/245)。
Expand Down
4 changes: 0 additions & 4 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,6 @@ https://leetcode.doocs.org/en
- [Find Critical and Pseudo-Critical Edges in Minimum Spanning Tree](/solution/1400-1499/1489.Find%20Critical%20and%20Pseudo-Critical%20Edges%20in%20Minimum%20Spanning%20Tree/README_EN.md) - `Minimum Spanning Tree`, `Kruskal's algorithm`, `Union find`
- [Is Graph Bipartite?](/solution/0700-0799/0785.Is%20Graph%20Bipartite/README_EN.md) - `Graph coloring`, `Union find`

<!--
### 7. Mathematical Knowledge
-->

## Contributions

I'm looking for long-term contributors/partners to this repo! Send me [PRs](https://github.com/doocs/leetcode/pulls) if you're interested! See the following:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,27 @@ function findThePrefixCommonArray(A: number[], B: number[]): number[] {
}
```

#### Rust

```rust
impl Solution {
pub fn find_the_prefix_common_array(a: Vec<i32>, b: Vec<i32>) -> Vec<i32> {
let n = a.len();
let mut ans = vec![0; n];
let mut cnt1 = vec![0; n + 1];
let mut cnt2 = vec![0; n + 1];
for i in 0..n {
cnt1[a[i] as usize] += 1;
cnt2[b[i] as usize] += 1;
for j in 1..=n {
ans[i] += std::cmp::min(cnt1[j], cnt2[j]);
}
}
ans
}
}
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down Expand Up @@ -296,6 +317,27 @@ function findThePrefixCommonArray(A: number[], B: number[]): number[] {
}
```

#### Rust

```rust
impl Solution {
pub fn find_the_prefix_common_array(a: Vec<i32>, b: Vec<i32>) -> Vec<i32> {
let n = a.len();
let mut ans = vec![0; n];
let mut vis = vec![1; n + 1];
let mut s = 0;
for i in 0..n {
vis[a[i] as usize] ^= 1;
s += vis[a[i] as usize];
vis[b[i] as usize] ^= 1;
s += vis[b[i] as usize];
ans[i] = s;
}
ans
}
}
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down Expand Up @@ -403,6 +445,23 @@ function bitCount64(i: bigint): number {
}
```

#### Rust

```rust
impl Solution {
pub fn find_the_prefix_common_array(a: Vec<i32>, b: Vec<i32>) -> Vec<i32> {
let mut ans = Vec::with_capacity(a.len());
let (mut x, mut y): (u64, u64) = (0, 0);
for (&a_val, &b_val) in a.iter().zip(b.iter()) {
x |= 1 << a_val;
y |= 1 << b_val;
ans.push((x & y).count_ones() as i32);
}
ans
}
}
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,27 @@ function findThePrefixCommonArray(A: number[], B: number[]): number[] {
}
```

#### Rust

```rust
impl Solution {
pub fn find_the_prefix_common_array(a: Vec<i32>, b: Vec<i32>) -> Vec<i32> {
let n = a.len();
let mut ans = vec![0; n];
let mut cnt1 = vec![0; n + 1];
let mut cnt2 = vec![0; n + 1];
for i in 0..n {
cnt1[a[i] as usize] += 1;
cnt2[b[i] as usize] += 1;
for j in 1..=n {
ans[i] += std::cmp::min(cnt1[j], cnt2[j]);
}
}
ans
}
}
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down Expand Up @@ -296,6 +317,27 @@ function findThePrefixCommonArray(A: number[], B: number[]): number[] {
}
```

#### Rust

```rust
impl Solution {
pub fn find_the_prefix_common_array(a: Vec<i32>, b: Vec<i32>) -> Vec<i32> {
let n = a.len();
let mut ans = vec![0; n];
let mut vis = vec![1; n + 1];
let mut s = 0;
for i in 0..n {
vis[a[i] as usize] ^= 1;
s += vis[a[i] as usize];
vis[b[i] as usize] ^= 1;
s += vis[b[i] as usize];
ans[i] = s;
}
ans
}
}
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down Expand Up @@ -403,6 +445,23 @@ function bitCount64(i: bigint): number {
}
```

#### Rust

```rust
impl Solution {
pub fn find_the_prefix_common_array(a: Vec<i32>, b: Vec<i32>) -> Vec<i32> {
let mut ans = Vec::with_capacity(a.len());
let (mut x, mut y): (u64, u64) = (0, 0);
for (&a_val, &b_val) in a.iter().zip(b.iter()) {
x |= 1 << a_val;
y |= 1 << b_val;
ans.push((x & y).count_ones() as i32);
}
ans
}
}
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
impl Solution {
pub fn find_the_prefix_common_array(a: Vec<i32>, b: Vec<i32>) -> Vec<i32> {
let n = a.len();
let mut ans = vec![0; n];
let mut cnt1 = vec![0; n + 1];
let mut cnt2 = vec![0; n + 1];
for i in 0..n {
cnt1[a[i] as usize] += 1;
cnt2[b[i] as usize] += 1;
for j in 1..=n {
ans[i] += std::cmp::min(cnt1[j], cnt2[j]);
}
}
ans
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
impl Solution {
pub fn find_the_prefix_common_array(a: Vec<i32>, b: Vec<i32>) -> Vec<i32> {
let n = a.len();
let mut ans = vec![0; n];
let mut vis = vec![1; n + 1];
let mut s = 0;
for i in 0..n {
vis[a[i] as usize] ^= 1;
s += vis[a[i] as usize];
vis[b[i] as usize] ^= 1;
s += vis[b[i] as usize];
ans[i] = s;
}
ans
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
impl Solution {
pub fn find_the_prefix_common_array(a: Vec<i32>, b: Vec<i32>) -> Vec<i32> {
let mut ans = Vec::with_capacity(a.len());
let (mut x, mut y): (u64, u64) = (0, 0);
for (&a_val, &b_val) in a.iter().zip(b.iter()) {
x |= 1 << a_val;
y |= 1 << b_val;
ans.push((x & y).count_ones() as i32);
}
ans
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,11 @@ tags:
<!-- solution:start -->

### 方法一:双 Deque 维护窗口最大值与最小值

本题要求计算所有长度不超过 $k$ 的子数组中:
* 子数组最大值之和
* 子数组最小值之和

- 子数组最大值之和
- 子数组最小值之和

并返回两者之和。

Expand All @@ -188,23 +190,28 @@ $$
表示所有以索引 $i$ 结尾的合法子数组,它们各自最小值的总和。

接下来,我们维护两个单调队列:
* `max_stack`:维护当前窗口内所有子数组可能的最大值。
* `min_stack`:维护当前窗口内所有子数组可能的最小值。

- `max_stack`:维护当前窗口内所有子数组可能的最大值。
- `min_stack`:维护当前窗口内所有子数组可能的最小值。

虽然底层数据结构为 `deque`,但由于绝大多数操作都发生在右侧,因此其本质仍然是单调栈。

队列中的每个元素格式为:

```text
(index, value, shares)
```

其中:
* `index`:元素索引
* `value`:元素值
* `shares`:该元素在当前窗口内,作为子数组最大值 / 最小值的贡献次数

- `index`:元素索引
- `value`:元素值
- `shares`:该元素在当前窗口内,作为子数组最大值 / 最小值的贡献次数

---

#### Step 1. 边界管理

当遍历到当前元素 `nums[i]` 时,我们首先需要确保窗口长度不超过 $k$。

窗口左边界为:
Expand All @@ -216,34 +223,41 @@ $$
若某元素索引已经小于该边界,说明其已经离开窗口,不再属于任何合法子数组。

因此:
* `max_stack` 栈头元素若已越界,则弹出
* `min_stack` 栈头元素若已越界,则弹出

- `max_stack` 栈头元素若已越界,则弹出
- `min_stack` 栈头元素若已越界,则弹出

同时,需要从:
* `subarrays_max_sum`
* `subarrays_min_sum`

- `subarrays_max_sum`
- `subarrays_min_sum`

中扣除这些元素对应的贡献。

---

#### Step 2. 更新单调栈

对于当前元素 `nums[i]`:

若 `max_stack` 栈尾元素的值小于等于 `nums[i]`:

说明这些元素已经不可能继续成为后续子数组的最大值。

因此:

1. 持续弹出这些元素
2. 将它们的贡献次数 `shares` 转移给 `nums[i]`
3. 更新 `subarrays_max_sum`

若弹出的元素为:

```text
(prev_idx, prev_num, prev_shares)
```

则会对:

```text
subarrays_max_sum
```
Expand All @@ -259,43 +273,54 @@ $$
随后,再加上当前元素自身形成的新子数组贡献。

最后,将:

```text
(i, nums[i], shares)
```

压入 `max_stack`。

---

`min_stack` 的操作方式完全相同。区别仅在于:
* `max_stack` 维护单调递减性质
* `min_stack` 维护单调递增性质

- `max_stack` 维护单调递减性质
- `min_stack` 维护单调递增性质

因此比较条件需要反转:

```text
max_stack: top_value <= nums[i]
min_stack: top_value >= nums[i]
```

---

#### Step 3. 累加答案

当索引 $i$ 处理完成后:

* `subarrays_max_sum`
- `subarrays_max_sum`
表示所有以 $i$ 结尾的合法子数组最大值总和
* `subarrays_min_sum`
- `subarrays_min_sum`
表示所有以 $i$ 结尾的合法子数组最小值总和

因此我们将两者加入最终答案:

```text
subarrays_max_min_sum
```

最终返回该结果即可。

---

时间复杂度为 $O(n)$,空间复杂度为 $O(n)$。

这是因为:
* 每个元素至多进入并离开 `max_stack` 各一次
* 每个元素至多进入并离开 `min_stack` 各一次

- 每个元素至多进入并离开 `max_stack` 各一次
- 每个元素至多进入并离开 `min_stack` 各一次

因此每个元素总操作次数不超过四次,整体时间复杂度为严格线性时间复杂度。

Expand Down
Loading