Skip to content

[pull] main from doocs:main #358

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 1 commit into from
Feb 13, 2025
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
26 changes: 12 additions & 14 deletions solution/0900-0999/0974.Subarray Sums Divisible by K/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,17 @@ tags:

### 方法一:哈希表 + 前缀和

假设存在 $i \leq j$,使得 $nums[i,..j]$ 的和能被 $k$ 整除,如果我们令 $s_i$ 表示 $nums[0,..i]$ 的和,令 $s_j$ 表示 $nums[0,..j]$ 的和,那么 $s_j - s_i$ 能被 $k$ 整除,即 $(s_j - s_i) \bmod k = 0$,也即 $s_j \bmod k = s_i \bmod k$。因此,我们可以用哈希表统计前缀和模 $k$ 的值的个数,从而快速判断是否存在满足条件的子数组。
假设存在 $i \leq j$,使得 $\textit{nums}[i,..j]$ 的和能被 $k$ 整除,如果我们令 $s_i$ 表示 $\textit{nums}[0,..i]$ 的和,令 $s_j$ 表示 $\textit{nums}[0,..j]$ 的和,那么 $s_j - s_i$ 能被 $k$ 整除,即 $(s_j - s_i) \bmod k = 0$,也即 $s_j \bmod k = s_i \bmod k$。因此,我们可以用哈希表统计前缀和模 $k$ 的值的个数,从而快速判断是否存在满足条件的子数组。

我们用一个哈希表 $cnt$ 统计前缀和模 $k$ 的值的个数,即 $cnt[i]$ 表示前缀和模 $k$ 的值为 $i$ 的个数。初始时 $cnt[0]=1$。用变量 $s$ 表示前缀和,初始时 $s = 0$。
我们用一个哈希表 $\textit{cnt}$ 统计前缀和模 $k$ 的值的个数,即 $\textit{cnt}[i]$ 表示前缀和模 $k$ 的值为 $i$ 的个数。初始时 $\textit{cnt}[0]=1$。用变量 $s$ 表示前缀和,初始时 $s = 0$。

接下来,从左到右遍历数组 $nums$,对于遍历到的每个元素 $x$,我们计算 $s = (s + x) \bmod k$,然后更新答案 $ans = ans + cnt[s]$,其中 $cnt[s]$ 表示前缀和模 $k$ 的值为 $s$ 的个数。最后我们将 $cnt[s]$ 的值加 $1$,继续遍历下一个元素。
接下来,从左到右遍历数组 $\textit{nums}$,对于遍历到的每个元素 $x$,我们计算 $s = (s + x) \bmod k$,然后更新答案 $\textit{ans} = \textit{ans} + \textit{cnt}[s]$,其中 $\textit{cnt}[s]$ 表示前缀和模 $k$ 的值为 $s$ 的个数。最后我们将 $\textit{cnt}[s]$ 的值加 $1$,继续遍历下一个元素。

最终,我们返回答案 $ans$。
最终,我们返回答案 $\textit{ans}$。

> 注意,由于 $s$ 的值可能为负数,因此我们可以将 $s$ 模 $k$ 的结果加上 $k$,再对 $k$ 取模,以确保 $s$ 的值为非负数。
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $nums$ 的长度。
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $\textit{nums}$ 的长度。

<!-- tabs:start -->

Expand Down Expand Up @@ -141,15 +141,13 @@ func subarraysDivByK(nums []int, k int) (ans int) {

```ts
function subarraysDivByK(nums: number[], k: number): number {
const counter = new Map();
counter.set(0, 1);
let s = 0,
ans = 0;
for (const num of nums) {
s += num;
const t = ((s % k) + k) % k;
ans += counter.get(t) || 0;
counter.set(t, (counter.get(t) || 0) + 1);
const cnt: { [key: number]: number } = { 0: 1 };
let s = 0;
let ans = 0;
for (const x of nums) {
s = (((s + x) % k) + k) % k;
ans += cnt[s] || 0;
cnt[s] = (cnt[s] || 0) + 1;
}
return ans;
}
Expand Down
40 changes: 13 additions & 27 deletions solution/0900-0999/0974.Subarray Sums Divisible by K/README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,29 +56,17 @@ tags:

### Solution 1: Hash Table + Prefix Sum

1. **Key Insight**:
Suppose there exists $i \leq j$ such that the sum of $\textit{nums}[i,..j]$ is divisible by $k$. If we let $s_i$ represent the sum of $\textit{nums}[0,..i]$ and $s_j$ represent the sum of $\textit{nums}[0,..j]$, then $s_j - s_i$ is divisible by $k$, i.e., $(s_j - s_i) \bmod k = 0$, which means $s_j \bmod k = s_i \bmod k$. Therefore, we can use a hash table to count the number of prefix sums modulo $k$, allowing us to quickly determine if there exists a subarray that meets the condition.

- If there exist indices $i$ and $j$ such that $i \leq j$, and the sum of the subarray $nums[i, ..., j]$ is divisible by $k$, then $(s_j - s_i) \bmod k = 0$, this implies: $s_j \bmod k = s_i \bmod k$
- We can use a hash table to count the occurrences of prefix sums modulo $k$ to efficiently check for subarrays satisfying the condition.
We use a hash table $\textit{cnt}$ to count the number of prefix sums modulo $k$, where $\textit{cnt}[i]$ represents the number of prefix sums with a modulo $k$ value of $i$. Initially, $\textit{cnt}[0] = 1$. We use a variable $s$ to represent the prefix sum, initially $s = 0$.

2. **Prefix Sum Modulo**:
Next, we traverse the array $\textit{nums}$ from left to right. For each element $x$, we calculate $s = (s + x) \bmod k$, then update the answer $\textit{ans} = \textit{ans} + \textit{cnt}[s]$, where $\textit{cnt}[s]$ represents the number of prefix sums with a modulo $k$ value of $s$. Finally, we increment the value of $\textit{cnt}[s]$ by $1$ and continue to the next element.

- Use a hash table $cnt$ to count occurrences of each prefix sum modulo $k$.
- $cnt[i]$ represents the number of prefix sums with modulo $k$ equal to $i$.
- Initialize $cnt[0] = 1$ to account for subarrays directly divisible by $k$.
In the end, we return the answer $\textit{ans}$.

3. **Algorithm**:
- Let a variable $s$ represent the running prefix sum, starting with $s = 0$.
- Traverse the array $nums$ from left to right.
- For each element $x$:
- Compute $s = (s + x) \bmod k$.
- Update the result: $ans += cnt[s]$.
- Increment $cnt[s]$ by $1$.
- Return the result $ans$.
> Note: Since the value of $s$ can be negative, we can add $k$ to the result of $s \bmod k$ and then take modulo $k$ again to ensure that the value of $s$ is non-negative.
> Note: if $s$ is negative, adjust it to be non-negative by adding $k$ and taking modulo $k$ again.
The time complexity is $O(n)$ and space complexity is $O(n)$ where $n$ is the length of the array $nums$.
The time complexity is $O(n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array $\textit{nums}$.

<!-- tabs:start -->

Expand Down Expand Up @@ -150,15 +138,13 @@ func subarraysDivByK(nums []int, k int) (ans int) {

```ts
function subarraysDivByK(nums: number[], k: number): number {
const counter = new Map();
counter.set(0, 1);
let s = 0,
ans = 0;
for (const num of nums) {
s += num;
const t = ((s % k) + k) % k;
ans += counter.get(t) || 0;
counter.set(t, (counter.get(t) || 0) + 1);
const cnt: { [key: number]: number } = { 0: 1 };
let s = 0;
let ans = 0;
for (const x of nums) {
s = (((s + x) % k) + k) % k;
ans += cnt[s] || 0;
cnt[s] = (cnt[s] || 0) + 1;
}
return ans;
}
Expand Down
16 changes: 7 additions & 9 deletions solution/0900-0999/0974.Subarray Sums Divisible by K/Solution.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
function subarraysDivByK(nums: number[], k: number): number {
const counter = new Map();
counter.set(0, 1);
let s = 0,
ans = 0;
for (const num of nums) {
s += num;
const t = ((s % k) + k) % k;
ans += counter.get(t) || 0;
counter.set(t, (counter.get(t) || 0) + 1);
const cnt: { [key: number]: number } = { 0: 1 };
let s = 0;
let ans = 0;
for (const x of nums) {
s = (((s + x) % k) + k) % k;
ans += cnt[s] || 0;
cnt[s] = (cnt[s] || 0) + 1;
}
return ans;
}
50 changes: 46 additions & 4 deletions solution/0900-0999/0980.Unique Paths III/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ tags:

<pre><strong>输入:</strong>[[1,0,0,0],[0,0,0,0],[0,0,0,2]]
<strong>输出:</strong>4
<strong>解释:</strong>我们有以下四条路径:
<strong>解释:</strong>我们有以下四条路径:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
Expand Down Expand Up @@ -269,9 +269,7 @@ function uniquePathsIII(grid: number[][]): number {
}
}
}
const vis: boolean[][] = Array(m)
.fill(0)
.map(() => Array(n).fill(false));
const vis: boolean[][] = Array.from({ length: m }, () => Array(n).fill(false));
vis[x][y] = true;
const dirs = [-1, 0, 1, 0, -1];
const dfs = (i: number, j: number, k: number): number => {
Expand All @@ -293,6 +291,50 @@ function uniquePathsIII(grid: number[][]): number {
}
```

#### JavaScript

```js
/**
* @param {number[][]} grid
* @return {number}
*/
var uniquePathsIII = function (grid) {
const m = grid.length;
const n = grid[0].length;
let [x, y] = [0, 0];
let cnt = 0;
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
if (grid[i][j] === 0) {
++cnt;
} else if (grid[i][j] === 1) {
[x, y] = [i, j];
}
}
}
const vis = Array.from({ length: m }, () => Array(n).fill(false));
vis[x][y] = true;
const dirs = [-1, 0, 1, 0, -1];
const dfs = function (i, j, k) {
if (grid[i][j] === 2) {
return k === cnt + 1 ? 1 : 0;
}
let ans = 0;
for (let d = 0; d < 4; ++d) {
const x = i + dirs[d];
const y = j + dirs[d + 1];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] !== -1) {
vis[x][y] = true;
ans += dfs(x, y, k + 1);
vis[x][y] = false;
}
}
return ans;
};
return dfs(x, y, 0);
};
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down
52 changes: 47 additions & 5 deletions solution/0900-0999/0980.Unique Paths III/README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ tags:
<pre>
<strong>Input:</strong> grid = [[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
<strong>Output:</strong> 2
<strong>Explanation:</strong> We have the following two paths:
<strong>Explanation:</strong> We have the following two paths:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)
2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)
</pre>
Expand All @@ -46,7 +46,7 @@ tags:
<pre>
<strong>Input:</strong> grid = [[1,0,0,0],[0,0,0,0],[0,0,0,2]]
<strong>Output:</strong> 4
<strong>Explanation:</strong> We have the following four paths:
<strong>Explanation:</strong> We have the following four paths:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
Expand Down Expand Up @@ -274,9 +274,7 @@ function uniquePathsIII(grid: number[][]): number {
}
}
}
const vis: boolean[][] = Array(m)
.fill(0)
.map(() => Array(n).fill(false));
const vis: boolean[][] = Array.from({ length: m }, () => Array(n).fill(false));
vis[x][y] = true;
const dirs = [-1, 0, 1, 0, -1];
const dfs = (i: number, j: number, k: number): number => {
Expand All @@ -298,6 +296,50 @@ function uniquePathsIII(grid: number[][]): number {
}
```

#### JavaScript

```js
/**
* @param {number[][]} grid
* @return {number}
*/
var uniquePathsIII = function (grid) {
const m = grid.length;
const n = grid[0].length;
let [x, y] = [0, 0];
let cnt = 0;
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
if (grid[i][j] === 0) {
++cnt;
} else if (grid[i][j] === 1) {
[x, y] = [i, j];
}
}
}
const vis = Array.from({ length: m }, () => Array(n).fill(false));
vis[x][y] = true;
const dirs = [-1, 0, 1, 0, -1];
const dfs = function (i, j, k) {
if (grid[i][j] === 2) {
return k === cnt + 1 ? 1 : 0;
}
let ans = 0;
for (let d = 0; d < 4; ++d) {
const x = i + dirs[d];
const y = j + dirs[d + 1];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] !== -1) {
vis[x][y] = true;
ans += dfs(x, y, k + 1);
vis[x][y] = false;
}
}
return ans;
};
return dfs(x, y, 0);
};
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down
39 changes: 39 additions & 0 deletions solution/0900-0999/0980.Unique Paths III/Solution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* @param {number[][]} grid
* @return {number}
*/
var uniquePathsIII = function (grid) {
const m = grid.length;
const n = grid[0].length;
let [x, y] = [0, 0];
let cnt = 0;
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
if (grid[i][j] === 0) {
++cnt;
} else if (grid[i][j] === 1) {
[x, y] = [i, j];
}
}
}
const vis = Array.from({ length: m }, () => Array(n).fill(false));
vis[x][y] = true;
const dirs = [-1, 0, 1, 0, -1];
const dfs = function (i, j, k) {
if (grid[i][j] === 2) {
return k === cnt + 1 ? 1 : 0;
}
let ans = 0;
for (let d = 0; d < 4; ++d) {
const x = i + dirs[d];
const y = j + dirs[d + 1];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] !== -1) {
vis[x][y] = true;
ans += dfs(x, y, k + 1);
vis[x][y] = false;
}
}
return ans;
};
return dfs(x, y, 0);
};
4 changes: 1 addition & 3 deletions solution/0900-0999/0980.Unique Paths III/Solution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ function uniquePathsIII(grid: number[][]): number {
}
}
}
const vis: boolean[][] = Array(m)
.fill(0)
.map(() => Array(n).fill(false));
const vis: boolean[][] = Array.from({ length: m }, () => Array(n).fill(false));
vis[x][y] = true;
const dirs = [-1, 0, 1, 0, -1];
const dfs = (i: number, j: number, k: number): number => {
Expand Down
10 changes: 5 additions & 5 deletions solution/0900-0999/0981.Time Based Key-Value Store/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ tags:

<strong>解释:</strong>
TimeMap timeMap = new TimeMap();
timeMap.set("foo", "bar", 1); // 存储键 "foo" 和值 "bar" ,时间戳 timestamp = 1 &nbsp;
timeMap.set("foo", "bar", 1); // 存储键 "foo" 和值 "bar" ,时间戳 timestamp = 1 &nbsp;
timeMap.get("foo", 1); // 返回 "bar"
timeMap.get("foo", 3); // 返回 "bar", 因为在时间戳 3 和时间戳 2 处没有对应 "foo" 的值,所以唯一的值位于时间戳 1 处(即 "bar") 。
timeMap.set("foo", "bar2", 4); // 存储键 "foo" 和值 "bar2" ,时间戳 timestamp = 4&nbsp;
timeMap.set("foo", "bar2", 4); // 存储键 "foo" 和值 "bar2" ,时间戳 timestamp = 4&nbsp;
timeMap.get("foo", 4); // 返回 "bar2"
timeMap.get("foo", 5); // 返回 "bar2"
</pre>
Expand All @@ -69,11 +69,11 @@ timeMap.get("foo", 5); // 返回 "bar2"

### 方法一:哈希表 + 有序集合(或二分查找)

我们可以用哈希表 $ktv$ 记录键值对,其中键为字符串 $key$,值为一个列表,列表中的每个元素为一个二元组 $(timestamp, value)$,表示键 $key$ 在时间戳 $timestamp$ 时对应的值为 $value$。
我们可以用哈希表 $\textit{kvt}$ 记录键值对,其中键为字符串 $\textit{key}$,值为一个有序集合,集合中的每个元素为一个二元组 $(\textit{timestamp}, \textit{value})$,表示键 $\textit{key}$ 在时间戳 $\textit{timestamp}$ 时对应的值为 $\textit{value}$。

当我们需要查询键 $key$ 在时间戳 $timestamp$ 时对应的值时,我们可以通过二分查找的方法在 $ktv[key]$ 中找到最大的时间戳 $timestamp'$,使得 $timestamp' \leq timestamp$,然后返回对应的值即可。
当我们需要查询键 $\textit{key}$ 在时间戳 $\textit{timestamp}$ 时对应的值时,我们可以通过有序集合的方法找到最大的时间戳 $\textit{timestamp}'$,使得 $\textit{timestamp}' \leq \textit{timestamp}$,然后返回对应的值即可。

时间复杂度方面,对于 $set$ 操作,由于哈希表的插入操作的时间复杂度为 $O(1)$,因此时间复杂度为 $O(1)$。对于 $get$ 操作,由于哈希表的查找操作的时间复杂度为 $O(1)$,而二分查找的时间复杂度为 $O(\log n)$,因此时间复杂度为 $O(\log n)$。空间复杂度为 $O(n)$,其中 $n$ 为 $set$ 操作的次数。
时间复杂度方面,对于 $\textit{set}$ 操作,由于哈希表的插入操作的时间复杂度为 $O(1)$,因此时间复杂度为 $O(1)$。对于 $\textit{get}$ 操作,由于哈希表的查找操作的时间复杂度为 $O(1)$,而有序集合的查找操作的时间复杂度为 $O(\log n)$,因此时间复杂度为 $O(\log n)$。空间复杂度为 $O(n)$,其中 $n$ 为 $\textit{set}$ 操作的次数。

<!-- tabs:start -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,13 @@ timeMap.get(&quot;foo&quot;, 5); // return &quot;bar2&quot;

<!-- solution:start -->

### Solution 1
### Solution 1: Hash Table + Ordered Set (or Binary Search)

We can use a hash table $\textit{kvt}$ to record key-value pairs, where the key is the string $\textit{key}$ and the value is an ordered set. Each element in the set is a tuple $(\textit{timestamp}, \textit{value})$, representing the value $\textit{value}$ corresponding to the key $\textit{key}$ at the timestamp $\textit{timestamp}$.

When we need to query the value corresponding to the key $\textit{key}$ at the timestamp $\textit{timestamp}$, we can use the ordered set to find the largest timestamp $\textit{timestamp}'$ such that $\textit{timestamp}' \leq \textit{timestamp}$, and then return the corresponding value.

In terms of time complexity, for the $\textit{set}$ operation, since the insertion operation of the hash table has a time complexity of $O(1)$, the time complexity is $O(1)$. For the $\textit{get}$ operation, since the lookup operation of the hash table has a time complexity of $O(1)$ and the lookup operation of the ordered set has a time complexity of $O(\log n)$, the time complexity is $O(\log n)$. The space complexity is $O(n)$, where $n$ is the number of $\textit{set}$ operations.

<!-- tabs:start -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class Solution {
class Solution {
public:
int countTriplets(vector<int>& nums) {
int mx = *max_element(nums.begin(), nums.end());
int mx = ranges::max(nums);
int cnt[mx + 1];
memset(cnt, 0, sizeof cnt);
for (int& x : nums) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class Solution {
class Solution {
public:
int countTriplets(vector<int>& nums) {
int mx = *max_element(nums.begin(), nums.end());
int mx = ranges::max(nums);
int cnt[mx + 1];
memset(cnt, 0, sizeof cnt);
for (int& x : nums) {
Expand Down
Loading