diff --git a/solution/1900-1999/1955.Count Number of Special Subsequences/README.md b/solution/1900-1999/1955.Count Number of Special Subsequences/README.md index b96be4d54015b..d03e72a664b8d 100644 --- a/solution/1900-1999/1955.Count Number of Special Subsequences/README.md +++ b/solution/1900-1999/1955.Count Number of Special Subsequences/README.md @@ -86,17 +86,17 @@ tags: 如果 $nums[i] = 0$:如果我们不选择 $nums[i]$,则 $f[i][0] = f[i-1][0]$;如果我们选择 $nums[i]$,那么 $f[i][0]=f[i-1][0]+1$,因为我们可以在任何一个以 $0$ 结尾的特殊子序列后面加上一个 $0$ 得到一个新的特殊子序列,也可以将 $nums[i]$ 单独作为一个特殊子序列。因此 $f[i][0] = 2 \times f[i - 1][0] + 1$。其余的 $f[i][j]$ 与 $f[i-1][j]$ 相等。 -如果 $nums[i] = 1$:如果我们不选择 $nums[i]$,则 $f[i][1] = f[i-1][1]$;如果我们选择 $nums[i]$,那么 $f[i][1]=f[i-1][1]+f[i-1][0]$,因为我们可以在任何一个以 $0$ 或 $1$ 结尾的特殊子序列后面加上一个 $1$ 得到一个新的特殊子序列。因此 $f[i][1] = f[i-1][1] + 2 \times f[i - 1][0]$。其余的 $f[i][j]$ 与 $f[i-1][j]$ 相等。 +如果 $nums[i] = 1$:如果我们不选择 $nums[i]$,则 $f[i][1] = f[i-1][1]$;如果我们选择 $nums[i]$,那么 $f[i][1]=f[i-1][1]+f[i-1][0]$,因为我们可以在任何一个以 $0$ 或 $1$ 结尾的特殊子序列后面加上一个 $1$ 得到一个新的特殊子序列。因此 $f[i][1] = f[i-1][0] + 2 \times f[i - 1][1]$。其余的 $f[i][j]$ 与 $f[i-1][j]$ 相等。 -如果 $nums[i] = 2$:如果我们不选择 $nums[i]$,则 $f[i][2] = f[i-1][2]$;如果我们选择 $nums[i]$,那么 $f[i][2]=f[i-1][2]+f[i-1][1]$,因为我们可以在任何一个以 $1$ 或 $2$ 结尾的特殊子序列后面加上一个 $2$ 得到一个新的特殊子序列。因此 $f[i][2] = f[i-1][2] + 2 \times f[i - 1][1]$。其余的 $f[i][j]$ 与 $f[i-1][j]$ 相等。 +如果 $nums[i] = 2$:如果我们不选择 $nums[i]$,则 $f[i][2] = f[i-1][2]$;如果我们选择 $nums[i]$,那么 $f[i][2]=f[i-1][2]+f[i-1][1]$,因为我们可以在任何一个以 $1$ 或 $2$ 结尾的特殊子序列后面加上一个 $2$ 得到一个新的特殊子序列。因此 $f[i][2] = f[i-1][1] + 2 \times f[i - 1][2]$。其余的 $f[i][j]$ 与 $f[i-1][j]$ 相等。 综上,我们可以得到如下的状态转移方程: $$ \begin{aligned} f[i][0] &= 2 \times f[i - 1][0] + 1, \quad nums[i] = 0 \\ -f[i][1] &= f[i-1][1] + 2 \times f[i - 1][0], \quad nums[i] = 1 \\ -f[i][2] &= f[i-1][2] + 2 \times f[i - 1][1], \quad nums[i] = 2 \\ +f[i][1] &= f[i-1][0] + 2 \times f[i - 1][1], \quad nums[i] = 1 \\ +f[i][2] &= f[i-1][1] + 2 \times f[i - 1][2], \quad nums[i] = 2 \\ f[i][j] &= f[i-1][j], \quad nums[i] \neq j \end{aligned} $$ @@ -105,8 +105,6 @@ $$ 时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 是数组 $nums$ 的长度。 -我们注意到,上述的状态转移方程中,$f[i][j]$ 的值仅与 $f[i-1][j]$ 有关,因此我们可以去掉第一维,将空间复杂度优化到 $O(1)$。 - #### Python3 @@ -258,7 +256,9 @@ function countSpecialSubsequences(nums: number[]): number { -### 方法二 +### 方法二:动态规划(空间优化) + +我们注意到,上述的状态转移方程中,$f[i][j]$ 的值仅与 $f[i-1][j]$ 有关,因此我们可以去掉第一维,将空间复杂度优化到 $O(1)$。 diff --git a/solution/1900-1999/1955.Count Number of Special Subsequences/README_EN.md b/solution/1900-1999/1955.Count Number of Special Subsequences/README_EN.md index 3d29305a5c3be..27c41bb546d5b 100644 --- a/solution/1900-1999/1955.Count Number of Special Subsequences/README_EN.md +++ b/solution/1900-1999/1955.Count Number of Special Subsequences/README_EN.md @@ -76,7 +76,34 @@ tags: -### Solution 1 +### Solution 1: Dynamic Programming + +We define $f[i][j]$ to represent the number of special subsequences ending with $j$ among the first $i+1$ elements. Initially, $f[i][j]=0$, and if $nums[0]=0$, then $f[0][0]=1$. + +For $i \gt 0$, we consider the value of $nums[i]$: + +If $nums[i] = 0$: If we do not choose $nums[i]$, then $f[i][0] = f[i-1][0]$; if we choose $nums[i]$, then $f[i][0]=f[i-1][0]+1$, because we can add a $0$ to the end of any special subsequence ending with $0$ to get a new special subsequence, or we can use $nums[i]$ alone as a special subsequence. Therefore, $f[i][0] = 2 \times f[i - 1][0] + 1$. The rest of $f[i][j]$ is equal to $f[i-1][j]$. + +If $nums[i] = 1$: If we do not choose $nums[i]$, then $f[i][1] = f[i-1][1]$; if we choose $nums[i]$, then $f[i][1]=f[i-1][1]+f[i-1][0]$, because we can add a $1$ to the end of any special subsequence ending with $0$ or $1$ to get a new special subsequence. Therefore, $f[i][1] = f[i-1][0] + 2 \times f[i - 1][1]$. The rest of $f[i][j]$ is equal to $f[i-1][j]$. + +If $nums[i] = 2$: If we do not choose $nums[i]$, then $f[i][2] = f[i-1][2]$; if we choose $nums[i]$, then $f[i][2]=f[i-1][2]+f[i-1][1]$, because we can add a $2$ to the end of any special subsequence ending with $1$ or $2$ to get a new special subsequence. Therefore, $f[i][2] = f[i-1][1] + 2 \times f[i - 1][2]$. The rest of $f[i][j]$ is equal to $f[i-1][j]$. + +In summary, we can get the following state transition equations: + +$$ +\begin{aligned} +f[i][0] &= 2 \times f[i - 1][0] + 1, \quad nums[i] = 0 \\ +f[i][1] &= f[i-1][0] + 2 \times f[i - 1][1], \quad nums[i] = 1 \\ +f[i][2] &= f[i-1][1] + 2 \times f[i - 1][2], \quad nums[i] = 2 \\ +f[i][j] &= f[i-1][j], \quad nums[i] \neq j +\end{aligned} +$$ + +The final answer is $f[n-1][2]$. + +The time complexity is $O(n)$, and the space complexity is $O(n)$. Where $n$ is the length of the array $nums$. + +Similar code found with 1 license type @@ -229,7 +256,13 @@ function countSpecialSubsequences(nums: number[]): number { -### Solution 2 +### Solution 2: Dynamic Programming (Space Optimization) + +We notice that in the above state transition equations, the value of $f[i][j]$ is only related to $f[i-1][j]$. Therefore, we can remove the first dimension and optimize the space complexity to $O(1)$. + +We can use an array $f$ of length 3 to represent the number of special subsequences ending with 0, 1, and 2, respectively. For each element in the array, we update the array $f$ according to the value of the current element. + +The time complexity is $O(n)$, and the space complexity is $O(1)$. Where $n$ is the length of the array $nums$. diff --git a/solution/3400-3499/3488.Closest Equal Element Queries/README.md b/solution/3400-3499/3488.Closest Equal Element Queries/README.md index efc5660e1af25..7764927be62c2 100644 --- a/solution/3400-3499/3488.Closest Equal Element Queries/README.md +++ b/solution/3400-3499/3488.Closest Equal Element Queries/README.md @@ -70,32 +70,198 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3400-3499/3488.Cl -### 方法一 +### 方法一:环形数组 + 哈希表 + +根据题目描述,我们需要找出数组每个元素与上一个相同元素的最小距离,以及与下一个相同元素的最小距离。并且,由于数组是循环的,所以我们需要考虑数组的环形特性,我们可以将数组扩展为原数组的两倍,然后使用哈希表 $\textit{left}$ 和 $\textit{right}$ 分别记录每个元素上一次出现的位置和下一次出现的位置,计算出每个位置的元素与另一个相同元素的最小距离,记录在数组 $\textit{d}$ 中。最后,我们遍历查询,对于每个查询 $i$,我们取 $\textit{d}[i]$ 和 $\textit{d}[i+n]$ 中的最小值,如果该值大于等于 $n$,则说明不存在与查询元素相同的元素,返回 $-1$,否则返回该值。 + +时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $\textit{nums}$ 的长度。 #### Python3 ```python - +class Solution: + def solveQueries(self, nums: List[int], queries: List[int]) -> List[int]: + n = len(nums) + m = n << 1 + d = [m] * m + left = {} + for i in range(m): + x = nums[i % n] + if x in left: + d[i] = min(d[i], i - left[x]) + left[x] = i + right = {} + for i in range(m - 1, -1, -1): + x = nums[i % n] + if x in right: + d[i] = min(d[i], right[x] - i) + right[x] = i + for i in range(n): + d[i] = min(d[i], d[i + n]) + return [-1 if d[i] >= n else d[i] for i in queries] ``` #### Java ```java - +class Solution { + public List solveQueries(int[] nums, int[] queries) { + int n = nums.length; + int m = n * 2; + int[] d = new int[m]; + Arrays.fill(d, m); + + Map left = new HashMap<>(); + for (int i = 0; i < m; i++) { + int x = nums[i % n]; + if (left.containsKey(x)) { + d[i] = Math.min(d[i], i - left.get(x)); + } + left.put(x, i); + } + + Map right = new HashMap<>(); + for (int i = m - 1; i >= 0; i--) { + int x = nums[i % n]; + if (right.containsKey(x)) { + d[i] = Math.min(d[i], right.get(x) - i); + } + right.put(x, i); + } + + for (int i = 0; i < n; i++) { + d[i] = Math.min(d[i], d[i + n]); + } + + List ans = new ArrayList<>(); + for (int query : queries) { + ans.add(d[query] >= n ? -1 : d[query]); + } + return ans; + } +} ``` #### C++ ```cpp - +class Solution { +public: + vector solveQueries(vector& nums, vector& queries) { + int n = nums.size(); + int m = n * 2; + vector d(m, m); + + unordered_map left; + for (int i = 0; i < m; i++) { + int x = nums[i % n]; + if (left.count(x)) { + d[i] = min(d[i], i - left[x]); + } + left[x] = i; + } + + unordered_map right; + for (int i = m - 1; i >= 0; i--) { + int x = nums[i % n]; + if (right.count(x)) { + d[i] = min(d[i], right[x] - i); + } + right[x] = i; + } + + for (int i = 0; i < n; i++) { + d[i] = min(d[i], d[i + n]); + } + + vector ans; + for (int query : queries) { + ans.push_back(d[query] >= n ? -1 : d[query]); + } + return ans; + } +}; ``` #### Go ```go +func solveQueries(nums []int, queries []int) []int { + n := len(nums) + m := n * 2 + d := make([]int, m) + for i := range d { + d[i] = m + } + + left := make(map[int]int) + for i := 0; i < m; i++ { + x := nums[i%n] + if idx, exists := left[x]; exists { + d[i] = min(d[i], i-idx) + } + left[x] = i + } + + right := make(map[int]int) + for i := m - 1; i >= 0; i-- { + x := nums[i%n] + if idx, exists := right[x]; exists { + d[i] = min(d[i], idx-i) + } + right[x] = i + } + + for i := 0; i < n; i++ { + d[i] = min(d[i], d[i+n]) + } + + ans := make([]int, len(queries)) + for i, query := range queries { + if d[query] >= n { + ans[i] = -1 + } else { + ans[i] = d[query] + } + } + return ans +} +``` +#### TypeScript + +```ts +function solveQueries(nums: number[], queries: number[]): number[] { + const n = nums.length; + const m = n * 2; + const d: number[] = Array(m).fill(m); + + const left = new Map(); + for (let i = 0; i < m; i++) { + const x = nums[i % n]; + if (left.has(x)) { + d[i] = Math.min(d[i], i - left.get(x)!); + } + left.set(x, i); + } + + const right = new Map(); + for (let i = m - 1; i >= 0; i--) { + const x = nums[i % n]; + if (right.has(x)) { + d[i] = Math.min(d[i], right.get(x)! - i); + } + right.set(x, i); + } + + for (let i = 0; i < n; i++) { + d[i] = Math.min(d[i], d[i + n]); + } + + return queries.map(query => (d[query] >= n ? -1 : d[query])); +} ``` diff --git a/solution/3400-3499/3488.Closest Equal Element Queries/README_EN.md b/solution/3400-3499/3488.Closest Equal Element Queries/README_EN.md index 3d239cf52726e..aa395d42cc443 100644 --- a/solution/3400-3499/3488.Closest Equal Element Queries/README_EN.md +++ b/solution/3400-3499/3488.Closest Equal Element Queries/README_EN.md @@ -68,32 +68,198 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3400-3499/3488.Cl -### Solution 1 +### Solution 1: Circular Array + Hash Table + +According to the problem description, we need to find the minimum distance between each element in the array and its previous identical element, as well as the minimum distance to its next identical element. Since the array is circular, we need to consider the circular nature of the array. We can extend the array to twice its original length, and then use hash tables $\textit{left}$ and $\textit{right}$ to record the positions where each element last appeared and will next appear, respectively. We calculate the minimum distance between each position's element and another identical element, recording it in the array $\textit{d}$. Finally, we traverse the queries, and for each query $i$, we take the minimum value of $\textit{d}[i]$ and $\textit{d}[i+n]$. If this value is greater than or equal to $n$, it means there is no element identical to the queried element, so we return $-1$; otherwise, we return the value. + +The time complexity is $O(n)$, and the space complexity is $O(n)$. Where $n$ is the length of the array $\textit{nums}$. #### Python3 ```python - +class Solution: + def solveQueries(self, nums: List[int], queries: List[int]) -> List[int]: + n = len(nums) + m = n << 1 + d = [m] * m + left = {} + for i in range(m): + x = nums[i % n] + if x in left: + d[i] = min(d[i], i - left[x]) + left[x] = i + right = {} + for i in range(m - 1, -1, -1): + x = nums[i % n] + if x in right: + d[i] = min(d[i], right[x] - i) + right[x] = i + for i in range(n): + d[i] = min(d[i], d[i + n]) + return [-1 if d[i] >= n else d[i] for i in queries] ``` #### Java ```java - +class Solution { + public List solveQueries(int[] nums, int[] queries) { + int n = nums.length; + int m = n * 2; + int[] d = new int[m]; + Arrays.fill(d, m); + + Map left = new HashMap<>(); + for (int i = 0; i < m; i++) { + int x = nums[i % n]; + if (left.containsKey(x)) { + d[i] = Math.min(d[i], i - left.get(x)); + } + left.put(x, i); + } + + Map right = new HashMap<>(); + for (int i = m - 1; i >= 0; i--) { + int x = nums[i % n]; + if (right.containsKey(x)) { + d[i] = Math.min(d[i], right.get(x) - i); + } + right.put(x, i); + } + + for (int i = 0; i < n; i++) { + d[i] = Math.min(d[i], d[i + n]); + } + + List ans = new ArrayList<>(); + for (int query : queries) { + ans.add(d[query] >= n ? -1 : d[query]); + } + return ans; + } +} ``` #### C++ ```cpp - +class Solution { +public: + vector solveQueries(vector& nums, vector& queries) { + int n = nums.size(); + int m = n * 2; + vector d(m, m); + + unordered_map left; + for (int i = 0; i < m; i++) { + int x = nums[i % n]; + if (left.count(x)) { + d[i] = min(d[i], i - left[x]); + } + left[x] = i; + } + + unordered_map right; + for (int i = m - 1; i >= 0; i--) { + int x = nums[i % n]; + if (right.count(x)) { + d[i] = min(d[i], right[x] - i); + } + right[x] = i; + } + + for (int i = 0; i < n; i++) { + d[i] = min(d[i], d[i + n]); + } + + vector ans; + for (int query : queries) { + ans.push_back(d[query] >= n ? -1 : d[query]); + } + return ans; + } +}; ``` #### Go ```go +func solveQueries(nums []int, queries []int) []int { + n := len(nums) + m := n * 2 + d := make([]int, m) + for i := range d { + d[i] = m + } + + left := make(map[int]int) + for i := 0; i < m; i++ { + x := nums[i%n] + if idx, exists := left[x]; exists { + d[i] = min(d[i], i-idx) + } + left[x] = i + } + + right := make(map[int]int) + for i := m - 1; i >= 0; i-- { + x := nums[i%n] + if idx, exists := right[x]; exists { + d[i] = min(d[i], idx-i) + } + right[x] = i + } + + for i := 0; i < n; i++ { + d[i] = min(d[i], d[i+n]) + } + + ans := make([]int, len(queries)) + for i, query := range queries { + if d[query] >= n { + ans[i] = -1 + } else { + ans[i] = d[query] + } + } + return ans +} +``` +#### TypeScript + +```ts +function solveQueries(nums: number[], queries: number[]): number[] { + const n = nums.length; + const m = n * 2; + const d: number[] = Array(m).fill(m); + + const left = new Map(); + for (let i = 0; i < m; i++) { + const x = nums[i % n]; + if (left.has(x)) { + d[i] = Math.min(d[i], i - left.get(x)!); + } + left.set(x, i); + } + + const right = new Map(); + for (let i = m - 1; i >= 0; i--) { + const x = nums[i % n]; + if (right.has(x)) { + d[i] = Math.min(d[i], right.get(x)! - i); + } + right.set(x, i); + } + + for (let i = 0; i < n; i++) { + d[i] = Math.min(d[i], d[i + n]); + } + + return queries.map(query => (d[query] >= n ? -1 : d[query])); +} ``` diff --git a/solution/3400-3499/3488.Closest Equal Element Queries/Solution.cpp b/solution/3400-3499/3488.Closest Equal Element Queries/Solution.cpp new file mode 100644 index 0000000000000..403bab8c89fcd --- /dev/null +++ b/solution/3400-3499/3488.Closest Equal Element Queries/Solution.cpp @@ -0,0 +1,36 @@ +class Solution { +public: + vector solveQueries(vector& nums, vector& queries) { + int n = nums.size(); + int m = n * 2; + vector d(m, m); + + unordered_map left; + for (int i = 0; i < m; i++) { + int x = nums[i % n]; + if (left.count(x)) { + d[i] = min(d[i], i - left[x]); + } + left[x] = i; + } + + unordered_map right; + for (int i = m - 1; i >= 0; i--) { + int x = nums[i % n]; + if (right.count(x)) { + d[i] = min(d[i], right[x] - i); + } + right[x] = i; + } + + for (int i = 0; i < n; i++) { + d[i] = min(d[i], d[i + n]); + } + + vector ans; + for (int query : queries) { + ans.push_back(d[query] >= n ? -1 : d[query]); + } + return ans; + } +}; diff --git a/solution/3400-3499/3488.Closest Equal Element Queries/Solution.go b/solution/3400-3499/3488.Closest Equal Element Queries/Solution.go new file mode 100644 index 0000000000000..5646b9a385734 --- /dev/null +++ b/solution/3400-3499/3488.Closest Equal Element Queries/Solution.go @@ -0,0 +1,40 @@ +func solveQueries(nums []int, queries []int) []int { + n := len(nums) + m := n * 2 + d := make([]int, m) + for i := range d { + d[i] = m + } + + left := make(map[int]int) + for i := 0; i < m; i++ { + x := nums[i%n] + if idx, exists := left[x]; exists { + d[i] = min(d[i], i-idx) + } + left[x] = i + } + + right := make(map[int]int) + for i := m - 1; i >= 0; i-- { + x := nums[i%n] + if idx, exists := right[x]; exists { + d[i] = min(d[i], idx-i) + } + right[x] = i + } + + for i := 0; i < n; i++ { + d[i] = min(d[i], d[i+n]) + } + + ans := make([]int, len(queries)) + for i, query := range queries { + if d[query] >= n { + ans[i] = -1 + } else { + ans[i] = d[query] + } + } + return ans +} diff --git a/solution/3400-3499/3488.Closest Equal Element Queries/Solution.java b/solution/3400-3499/3488.Closest Equal Element Queries/Solution.java new file mode 100644 index 0000000000000..c702ea7539a5e --- /dev/null +++ b/solution/3400-3499/3488.Closest Equal Element Queries/Solution.java @@ -0,0 +1,36 @@ +class Solution { + public List solveQueries(int[] nums, int[] queries) { + int n = nums.length; + int m = n * 2; + int[] d = new int[m]; + Arrays.fill(d, m); + + Map left = new HashMap<>(); + for (int i = 0; i < m; i++) { + int x = nums[i % n]; + if (left.containsKey(x)) { + d[i] = Math.min(d[i], i - left.get(x)); + } + left.put(x, i); + } + + Map right = new HashMap<>(); + for (int i = m - 1; i >= 0; i--) { + int x = nums[i % n]; + if (right.containsKey(x)) { + d[i] = Math.min(d[i], right.get(x) - i); + } + right.put(x, i); + } + + for (int i = 0; i < n; i++) { + d[i] = Math.min(d[i], d[i + n]); + } + + List ans = new ArrayList<>(); + for (int query : queries) { + ans.add(d[query] >= n ? -1 : d[query]); + } + return ans; + } +} diff --git a/solution/3400-3499/3488.Closest Equal Element Queries/Solution.py b/solution/3400-3499/3488.Closest Equal Element Queries/Solution.py new file mode 100644 index 0000000000000..9e16ab46c683b --- /dev/null +++ b/solution/3400-3499/3488.Closest Equal Element Queries/Solution.py @@ -0,0 +1,20 @@ +class Solution: + def solveQueries(self, nums: List[int], queries: List[int]) -> List[int]: + n = len(nums) + m = n << 1 + d = [m] * m + left = {} + for i in range(m): + x = nums[i % n] + if x in left: + d[i] = min(d[i], i - left[x]) + left[x] = i + right = {} + for i in range(m - 1, -1, -1): + x = nums[i % n] + if x in right: + d[i] = min(d[i], right[x] - i) + right[x] = i + for i in range(n): + d[i] = min(d[i], d[i + n]) + return [-1 if d[i] >= n else d[i] for i in queries] diff --git a/solution/3400-3499/3488.Closest Equal Element Queries/Solution.ts b/solution/3400-3499/3488.Closest Equal Element Queries/Solution.ts new file mode 100644 index 0000000000000..5c7110e1d2b97 --- /dev/null +++ b/solution/3400-3499/3488.Closest Equal Element Queries/Solution.ts @@ -0,0 +1,29 @@ +function solveQueries(nums: number[], queries: number[]): number[] { + const n = nums.length; + const m = n * 2; + const d: number[] = Array(m).fill(m); + + const left = new Map(); + for (let i = 0; i < m; i++) { + const x = nums[i % n]; + if (left.has(x)) { + d[i] = Math.min(d[i], i - left.get(x)!); + } + left.set(x, i); + } + + const right = new Map(); + for (let i = m - 1; i >= 0; i--) { + const x = nums[i % n]; + if (right.has(x)) { + d[i] = Math.min(d[i], right.get(x)! - i); + } + right.set(x, i); + } + + for (let i = 0; i < n; i++) { + d[i] = Math.min(d[i], d[i + n]); + } + + return queries.map(query => (d[query] >= n ? -1 : d[query])); +}