Skip to content

Commit aee5cd9

Browse files
committed
feat: 20260225 check in
1 parent 5b94e0b commit aee5cd9

4 files changed

Lines changed: 222 additions & 63 deletions

File tree

leetcode/3-动态规划(基础版)/3-动态规划在字符串的应用/115. 不同的子序列.md

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
3. 边界条件:
1919
- 如果$j < 0$,说明t已经匹配完成,返回1
20-
- 如果$i < 0$或$i < j$,说明s已经用完但t还没匹配完,返回0
20+
- 如果$i < 0$,说明s已经用完但t还没匹配完,返回0
2121

2222
- 时间复杂度:$O(nm)$,其中 $n$ 和 $m$ 分别是 $s$ 和 $t$ 的长度
2323
- 空间复杂度:$O(nm)$,主要是记忆化数组的开销
@@ -31,7 +31,7 @@ class Solution:
3131
def dfs(i, j):
3232
if j < 0:
3333
return 1
34-
if i < 0 or i < j:
34+
if i < 0:
3535
return 0
3636
if s[i] != t[j]:
3737
return dfs(i - 1, j)
@@ -96,15 +96,14 @@ public:
9696
class Solution:
9797
def numDistinct(self, s: str, t: str) -> int:
9898
n, m = len(s), len(t)
99-
f = [[0] * (m + 1) for _ in range(n + 1)]
100-
for i in range(n + 1):
101-
f[i][0] = 1
102-
103-
for i in range(n):
104-
for j in range(m):
105-
f[i + 1][j + 1] = f[i][j + 1]
106-
if s[i] == t[j]:
107-
f[i + 1][j + 1] += f[i][j]
99+
f = [[1] + [0] * m for _ in range(n + 1)]
100+
101+
for i, c1 in enumerate(s):
102+
for j, c2 in enumerate(t):
103+
if c1 != c2:
104+
f[i + 1][j + 1] = f[i][j + 1]
105+
else:
106+
f[i + 1][j + 1] = f[i][j + 1] + f[i][j]
108107
return f[n][m]
109108
```
110109

@@ -117,6 +116,4 @@ class Solution:
117116
1. $f[i][j]$ 表示区间 $[i, j]$ 内字符串 t 出现的次数,但是这种表示太不方便计算了,感觉不对
118117
2. 想不出来 $f[i][j]$ 还有什么状态表示了
119118

120-
然后看了题解,发现这道题的难点在于状态表示,如果状态表示对了,那么这道题就很简单了。
121-
122-
这道题的难点在于状态表示,如果状态表示对了,那么这道题就很简单了。
119+
然后看了题解,发现这道题的难点在于状态表示,如果状态表示对了,那么这道题就很简单了。
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# [1356. 根据数字二进制下 1 的数目排序](https://leetcode.cn/problems/sort-integers-by-the-number-of-1-bits/description/)
2+
3+
> **日期**:2026-02-25
4+
> **所用时间**:1min
5+
> **知识点**:排序、位运算
6+
7+
## 1. 题目描述
8+
9+
给你一个整数数组 `arr`。请你将数组中的元素按照其二进制表示中数字 **1** 的数目升序排序。
10+
11+
若存在多个数字二进制中 **1** 的数目相同,则必须将它们按照数值大小升序排列。
12+
13+
请你返回排序后的数组。
14+
15+
**示例 1:**
16+
17+
- **输入**:arr = [0,1,2,3,4,5,6,7,8]
18+
- **输出**[0,1,2,4,8,3,5,6,7]
19+
- **解释**:二进制中 1 的个数分别为 0,1,1,2,1,2,2,3,1,按个数排序后再按数值排序得到 [0,1,2,4,8,3,5,6,7]
20+
21+
**示例 2:**
22+
23+
- **输入**:arr = [1024,512,256,128,64,32,16,8,4,2,1]
24+
- **输出**[1,2,4,8,16,32,64,128,256,512,1024]
25+
26+
**提示:**
27+
28+
- `1 <= arr.length <= 500`
29+
- `0 <= arr[i] <= 10^4`
30+
31+
## 2. 排序
32+
33+
自定义排序:以「二进制中 1 的个数」为第一关键字升序,「数值」为第二关键字升序。Python 中可用 `sorted(arr, key=lambda x: (x.bit_count(), x))`,其中 `bit_count()` 为 Python 3.10+ 内置方法;
34+
35+
若环境不支持 `bit_count()` 方法,可用 `bin(x).count('1')` 替代。
36+
37+
复杂度分析:
38+
39+
- 时间复杂度:$O(n\log n)$,其中 $n$ 为数组长度,排序主导。
40+
- 空间复杂度:$O(\log n)$,排序栈空间(若不计返回数组)。
41+
42+
**Python3**
43+
44+
```python
45+
class Solution:
46+
def sortByBits(self, arr: List[int]) -> List[int]:
47+
return sorted(arr, key=lambda x: (x.bit_count(), x))
48+
```
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# [3714. 最长的平衡子串 II](https://leetcode.cn/problems/longest-balanced-substring-ii/description/)
2+
3+
> **日期**:2026-02-19
4+
> **知识点**:前缀和、哈希表
5+
6+
## 1. 题目描述
7+
8+
给定一个字符串 `s`,请找出其中最长的**平衡子串**的长度。
9+
10+
**平衡子串**的定义:子串中所有出现过的字符,出现次数都相同。
11+
12+
例如:
13+
- `"ab"``"aabb"``"abc"``"aabbcc"` 都是平衡的(每种字符出现次数相等);
14+
- `"aab"` 不是平衡的(a 出现 2 次,b 出现 1 次)。
15+
16+
**约束:**
17+
- `1 <= s.length <= 10^5`(或题目给定)
18+
- `s` 仅由小写英文字母组成(最多 26 种字符,本题可只考虑 a/b/c 三类)
19+
20+
**示例 1:**
21+
22+
```
23+
输入:s = "abc"
24+
输出:3
25+
解释:整个串 "abc" 中 a、b、c 各出现 1 次,是平衡的,长度为 3。
26+
```
27+
28+
**示例 2:**
29+
30+
```
31+
输入:s = "aabb"
32+
输出:4
33+
解释:"aabb" 中 a 和 b 各出现 2 次,是平衡的,长度为 4。
34+
```
35+
36+
**示例 3:**
37+
38+
```
39+
输入:s = "aaa"
40+
输出:1
41+
解释:仅一种字符时,可视为出现次数相同,最长平衡子串长度为 1。
42+
```
43+
44+
## 2. 分类讨论(单字符 / 两字符 / 三字符)+ 前缀和与哈希表
45+
46+
平衡子串只可能由**一种、两种或三种**字符组成,分三类处理:
47+
48+
- **情况一(单字符)**:最长连续相同字符段,扫一遍用 `last` 维护当前段长,更新最大值。
49+
- **情况二(两种字符 x, y)**:只含 x、y 的极长段内,将 x 视为 +1、y 视为 -1 做前缀差 `diff`;相同 `diff` 的两位置之间即平衡子串。用哈希表记录每个 `diff` 首次出现位置,枚举右端点更新答案。对 (a,b)、(b,c)、(a,c) 各跑一遍取最大。
50+
- **情况三(三种字符 a,b,c)**:用 `pre[0],pre[1],pre[2]` 表示 a,b,c 的前缀个数,平衡等价于 `pre[i]-pre[j]` 在两维上都相等。以 `(pre[0]-pre[1], pre[1]-pre[2])` 为 key 存首次下标,枚举右端点更新答案。
51+
52+
复杂度分析:
53+
54+
- **时间复杂度**:$O(n)$,单字符与三字符各一遍扫描,两字符对三组 (x,y) 各 $O(n)$。
55+
- **空间复杂度**:$O(n)$ 或 $O(1)$(哈希表 key 数量有界)。
56+
57+
**Python3**
58+
59+
```python
60+
class Solution:
61+
def longestBalanced(self, s: str) -> int:
62+
n = len(s)
63+
ans = 0
64+
65+
# 情况一
66+
last = 0
67+
for i in range(n):
68+
last = last + 1 if i > 0 and s[i] == s[i - 1] else 1
69+
ans = max(ans, last)
70+
71+
# 情况二
72+
def work(x, y):
73+
ans = 0
74+
i = 0
75+
while i < n:
76+
if s[i] not in (x, y):
77+
i += 1
78+
continue
79+
pos = {0: i - 1}
80+
diff = 0
81+
while i < n and s[i] in (x, y):
82+
diff += 1 if s[i] == x else -1
83+
if diff in pos:
84+
ans = max(ans, i - pos[diff])
85+
else:
86+
pos[diff] = i
87+
i += 1
88+
i += 1
89+
return ans
90+
91+
ans = max(ans, work('a', 'b'), work('b', 'c'), work('a', 'c'))
92+
93+
# 情况三
94+
pre = [0, 0, 0]
95+
pos = {(0, 0): -1}
96+
for i, c in enumerate(s):
97+
pre[ord(c) - 97] += 1
98+
key = (pre[0] - pre[1], pre[1] - pre[2])
99+
if key in pos:
100+
ans = max(ans, i - pos[key])
101+
else:
102+
pos[key] = i
103+
return ans
104+
```
Lines changed: 59 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,49 @@
1-
# [3290. 最高乘法得分](https://leetcode.cn/problems/maximum-multiplication-score/description/)
1+
# 3290. 最高乘法得分
22

3-
> **日期:** 2024-09-15
4-
> **所用时间:** 12min
3+
> **日期**:2024-09-15
4+
> **所用时间**:12min
5+
> **知识点**:动态规划、记忆化搜索
56
6-
## 1. 记忆化搜索
7+
## 1. 题目描述
78

8-
状态表示:$dfs(i, j)$ 表示在区间 $[0, i]$ 内选择 $j$ 个数
9+
给定两个整数数组 `a``b`,长度分别为 `n``m`(保证 $m \ge n$)。
910

10-
状态计算:对于第$i$个数来说,有两种做法:选和不选
11-
12-
1. 选第$i$个数,则$dfs(i - 1, j - 1) + a[i] \times b[j - 1]$
13-
2. 不选,则$dfs(i - 1, j)$
14-
15-
最终的状态计算方程如下:
11+
你需要从 `b`**按顺序**选出恰好 `n` 个下标 $j_0 < j_1 < \cdots < j_{n-1}$,并将 `a[i]``b[j_i]` 一一配对。**乘法得分**定义为所有配对乘积之和:
1612

1713
$$
18-
\max(dfs(i - 1, j), dfs(i - 1, j - 1) + a[i] \times b[j - 1])
14+
\sum_{i=0}^{n-1} a[i] \times b[j_i]
1915
$$
2016

17+
求可能得到的**最高乘法得分**
18+
19+
**示例 1:**
20+
21+
- **输入**:a = [2, 1, 3], b = [1, 2, 3, 4]
22+
- **输出**:20
23+
- **解释**:从 b 中按顺序选 3 个下标与 a 配对,使乘积和最大即可得到 20。
24+
25+
**示例 2:**
26+
27+
- **输入**:a = [1, 2], b = [1, 2, 3]
28+
- **输出**:8
29+
- **解释**:选 b 的下标 [1, 2],得分为 1*2 + 2*3 = 8。
30+
31+
**提示:**
32+
33+
- $1 \le n \le m$
34+
- $1 \le n, m \le 500$
35+
- 数组元素为整数(可能为负,以题目约束为准)。
36+
37+
## 2 记忆化搜索
38+
39+
状态:$dfs(i, j)$ 表示在数组 `a` 的前 $i+1$ 个元素与数组 `b` 的前 $j+1$ 个元素中,已按顺序配对完 $a[0..i]$ 与某 $j+1$ 个位置中的 $i+1$ 个来自 `b` 的元素时,能得到的最大得分(即从 `b[0..j]` 里选恰好 $i+1$ 个做配对)。
40+
41+
- **当 $i < 0$**:无元素可配,返回 $0$。
42+
- **当 $i = j$**`a[0..i]``b[0..j]` 长度均为 $i+1$,必须一一配对,故 $dfs(i, j) = dfs(i-1, j-1) + a[i] \times b[j]$。
43+
- **当 $i \neq j$**:要么将 $a[i]$ 与 $b[j]$ 配对,得 $dfs(i-1, j-1) + a[i] \times b[j]$;要么不选 $b[j]$,得 $dfs(i, j-1)$。取两者最大值。
44+
45+
复杂度分析:
46+
2147
- 时间复杂度:$O(nm)$
2248
- 空间复杂度:$O(nm)$
2349

@@ -28,42 +54,24 @@ class Solution:
2854
def maxScore(self, a: List[int], b: List[int]) -> int:
2955
@cache
3056
def dfs(i, j):
31-
if j == 0:
32-
return 0
3357
if i < 0:
34-
return -inf
35-
return max(dfs(i - 1, j), dfs(i - 1, j - 1) + a[j - 1] * b[i])
36-
return dfs(len(b) - 1, 4)
58+
return 0
59+
if i == j:
60+
return dfs(i - 1, j - 1) + a[i] * b[j]
61+
return max(dfs(i - 1, j - 1) + a[i] * b[j], dfs(i, j - 1))
62+
return dfs(len(a) - 1, len(b) - 1)
3763
```
3864

39-
## 2. 第二种记忆化搜索的状态定义
40-
41-
### 状态表示
42-
43-
$dfs(i, j, k)$ 表示在数组 $a$ 的前 $i$ 个元素和数组 $b$ 的前 $j$ 个元素中选择 $k$ 个数
44-
45-
### 状态计算
46-
47-
对于第 $i$ 个元素和第 $j$ 个元素来说,有两种情况:
65+
## 3 递推(DP 表)
4866

49-
1. 如果 $i = j$,则必须同时选择这两个元素,状态转移为 $dfs(i - 1, j - 1, k - 1) + a[i] \times b[j]$
50-
2. 如果 $i \neq j$,则有两种选择:
51-
- 不选择数组 $b$ 的第 $j$ 个元素,状态转移为 $dfs(i, j - 1, k)$
52-
- 同时选择数组 $a$ 的第 $i$ 个元素和数组 $b$ 的第 $j$ 个元素,状态转移为 $dfs(i - 1, j - 1, k - 1) + a[i] \times b[j]$
67+
将上述记忆化搜索改为递推:$f[i][j]$ 表示考虑 `a` 的前 $i$ 个元素与 `b` 的前 $j$ 个元素时能得到的最大得分(即从 `b[0..j-1]` 中选恰好 $i$ 个与 `a[0..i-1]` 配对的最大和)。
5368

54-
### 边界条件
69+
- 当 $i = j$:必须配对 $a[i-1]$ 与 $b[j-1]$,故 $f[i][j] = f[i-1][j-1] + a[i-1] \times b[j-1]$。
70+
- 当 $i \neq j$:要么配对 $a[i-1]$ 与 $b[j-1]$,得 $f[i-1][j-1] + a[i-1] \times b[j-1]$;要么不选 $b[j-1]$,得 $f[i][j-1]$。取最大值。
5571

56-
- 当 $k = 0$ 时,说明已经选择了 $k$ 个数,返回 0
57-
- 当 $i < 0$ 或 $j < 0$ 时,说明数组 $a$ 或 $b$ 已经遍历完了,返回 0
72+
按 $i$、$j$ 递增递推,答案为 $f[n][m]$。
5873

59-
### 状态计算方程
60-
61-
$$
62-
\begin{cases}
63-
dfs(i - 1, j - 1, k - 1) + a[i] \times b[j], & \text{if } i = j \\
64-
\max(dfs(i, j - 1, k), dfs(i - 1, j - 1, k - 1) + a[i] \times b[j]), & \text{if } i \neq j
65-
\end{cases}
66-
$$
74+
复杂度分析:
6775

6876
- 时间复杂度:$O(nm)$
6977
- 空间复杂度:$O(nm)$
@@ -73,12 +81,14 @@ $$
7381
```python
7482
class Solution:
7583
def maxScore(self, a: List[int], b: List[int]) -> int:
76-
@cache
77-
def dfs(i, j, k):
78-
if k == 0 or i < 0 or j < 0:
79-
return 0
80-
if i == j:
81-
return dfs(i - 1, j - 1, k - 1) + a[i] * b[j]
82-
return max(dfs(i, j - 1, k), dfs(i - 1, j - 1, k - 1) + a[i] * b[j])
83-
return dfs(len(a) - 1, len(b) - 1, 4)
84+
n, m = len(a), len(b)
85+
f = [[0] * (m + 1) for _ in range(n + 1)]
86+
87+
for i, x in enumerate(a):
88+
for j, y in enumerate(b):
89+
if i == j:
90+
f[i + 1][j + 1] = f[i][j] + x * y
91+
else:
92+
f[i + 1][j + 1] = max(f[i][j] + x * y, f[i + 1][j])
93+
return f[n][m]
8494
```

0 commit comments

Comments
 (0)