Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit fa6be57

Browse files
✨feat: add 940
1 parent 116fb52 commit fa6be57

File tree

2 files changed

+197
-0
lines changed

2 files changed

+197
-0
lines changed

‎Index/序列 DP.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
| [740. 删除并获得点数](https://leetcode-cn.com/problems/delete-and-earn/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/delete-and-earn/solution/gong-shui-san-xie-zhuan-huan-wei-xu-lie-6c9t0/) | 中等 | 🤩🤩🤩🤩🤩 |
1616
| [873. 最长的斐波那契子序列的长度](https://leetcode.cn/problems/length-of-longest-fibonacci-subsequence/) | [LeetCode 题解链接](https://leetcode.cn/problems/length-of-longest-fibonacci-subsequence/solution/by-ac_oier-beo2/) | 中等 | 🤩🤩🤩🤩🤩 |
1717
| [926. 将字符串翻转到单调递增](https://leetcode.cn/problems/flip-string-to-monotone-increasing/) | [LeetCode 题解链接](https://leetcode.cn/problems/flip-string-to-monotone-increasing/solution/by-ac_oier-h0we/) | 中等 | 🤩🤩🤩🤩🤩 |
18+
| [940. 不同的子序列 II](https://leetcode.cn/problems/distinct-subsequences-ii/) | [LeetCode 题解链接](https://leetcode.cn/problems/distinct-subsequences-ii/solution/by-ac_oier-ph94/) | 困难 | 🤩🤩🤩🤩🤩 |
1819
| [978. 最长湍流子数组](https://leetcode-cn.com/problems/longest-turbulent-subarray/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/longest-turbulent-subarray/solution/xiang-jie-dong-tai-gui-hua-ru-he-cai-dp-3spgj/) | 中等 | 🤩🤩🤩 |
1920
| [1035. 不相交的线](https://leetcode-cn.com/problems/uncrossed-lines/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/uncrossed-lines/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-bkaas/) | 中等 | 🤩🤩🤩🤩 |
2021
| [1092. 最短公共超序列](https://leetcode.cn/problems/shortest-common-supersequence/) | [LeetCode 题解链接](https://leetcode.cn/problems/shortest-common-supersequence/solution/by-ac_oier-s346/) | 困难 | 🤩🤩🤩🤩 |
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[940. 不同的子序列 II](https://leetcode.cn/problems/distinct-subsequences-ii/solution/by-ac_oier-ph94/)** ,难度为 **困难**
4+
5+
Tag : 「序列 DP」、「动态规划」
6+
7+
8+
9+
给定一个字符串 `s`,计算 `s` 的 不同非空子序列 的个数。因为结果可能很大,所以返回答案需要对 10ドル^9 + 7$ 取余 。
10+
11+
字符串的 子序列 是经由原字符串删除一些(也可能不删除)字符但不改变剩余字符相对位置的一个新字符串。
12+
13+
例如,`"ace"``"abcde"` 的一个子序列,但 `"aec"` 不是。
14+
15+
示例 1:
16+
```
17+
输入:s = "abc"
18+
19+
输出:7
20+
21+
解释:7 个不同的子序列分别是 "a", "b", "c", "ab", "ac", "bc", 以及 "abc"。
22+
```
23+
示例 2:
24+
```
25+
输入:s = "aba"
26+
27+
输出:6
28+
29+
解释:6 个不同的子序列分别是 "a", "b", "ab", "ba", "aa" 以及 "aba"。
30+
```
31+
示例 3:
32+
```
33+
输入:s = "aaa"
34+
35+
输出:3
36+
37+
解释:3 个不同的子序列分别是 "a", "aa" 以及 "aaa"。
38+
```
39+
40+
提示:
41+
* 1ドル <= s.length <= 2000$
42+
* `s` 仅由小写英文字母组成
43+
44+
---
45+
46+
### 序列 DP
47+
48+
为了方便,我们令 `s` 下标从 1ドル$ 开始,定义 $f[i][j]$ 为考虑前 $i$ 个字符,且结尾字符为 $j$ 的不同子序列的个数,其中 $j$ 的范围为 $[0, 25]$ 代指小写字符 `a-z`
49+
50+
我们有显而易见的初始化条件 $f[0][X] = 0,ドル最终答案为 $\sum_{i = 0}^{25}f[n][i]$。
51+
52+
不失一般性考虑 $f[i][j]$ 该如何转移,根据 $s[i]$ 是否为 $j$ 进行分情况讨论:
53+
54+
* $s[i] \neq j$ : 由于状态定义限定了结尾字符必须是 $j,ドル因而 $s[i]$ 必然不会用到,此时有:
55+
56+
$$
57+
f[i][j] = f[i - 1][j]
58+
$$
59+
60+
* $s[i] = j$ : 此时 $s[i]$ 可作为结尾元素,同时由于我们统计的是「不同」的子序列个数,因而「以 $j$ 结尾的子序列方案数」与「以 $s[i]$ 结尾的子序列方案数」完全等价。
61+
对于以 $s[i]$ 作为子序列结尾字符的方案数,容易想到其方案数等于「$s[i]$ 单独作为子序列」+「$s[i]$ 拼接在其余子序列后面形成新子序列」,即有:
62+
63+
$$
64+
f[i][j] = 1 + \sum_{k = 0}^{25} f[i - 1][k]
65+
$$
66+
67+
Java 代码:
68+
```Java
69+
class Solution {
70+
int MOD = (int)1e9+7;
71+
public int distinctSubseqII(String s) {
72+
int n = s.length(), ans = 0;
73+
int[][] f = new int[n + 1][26];
74+
for (int i = 1; i <= n; i++) {
75+
int c = s.charAt(i - 1) - 'a';
76+
for (int j = 0; j < 26; j++) {
77+
if (c != j) {
78+
f[i][j] = f[i - 1][j];
79+
} else {
80+
int cur = 1;
81+
for (int k = 0; k < 26; k++) cur = (cur + f[i - 1][k]) % MOD;
82+
f[i][j] = cur;
83+
}
84+
}
85+
}
86+
for (int i = 0; i < 26; i++) ans = (ans + f[n][i]) % MOD;
87+
return ans;
88+
}
89+
}
90+
```
91+
TypeScript 代码:
92+
```TypeScript
93+
function distinctSubseqII(s: string): number {
94+
const MOD = 1e9+7
95+
let n = s.length, ans = 0
96+
const f = new Array<Array<number>>(n + 1)
97+
for (let i = 0; i <= n; i++) f[i] = new Array<number>(26).fill(0)
98+
for (let i = 1; i <= n; i++) {
99+
const c = s.charCodeAt(i - 1) - 'a'.charCodeAt(0)
100+
for (let j = 0; j < 26; j++) {
101+
if (c != j) {
102+
f[i][j] = f[i - 1][j]
103+
} else {
104+
let cur = 1
105+
for (let k = 0; k < 26; k++) cur = (cur + f[i - 1][k]) % MOD
106+
f[i][j] = cur
107+
}
108+
}
109+
}
110+
for (let i = 0; i < 26; i++) ans = (ans + f[n][i]) % MOD
111+
return ans
112+
}
113+
```
114+
Python 代码:
115+
```Python
116+
class Solution:
117+
def distinctSubseqII(self, s: str) -> int:
118+
n, MOD = len(s), 1e9+7
119+
f = [[0] * 26 for _ in range(n + 1)]
120+
for i in range(1, n + 1):
121+
c = ord(s[i - 1]) - ord('a')
122+
for j in range(26):
123+
f[i][j] = f[i - 1][j] if c != j else (1 + sum(f[i - 1])) % MOD
124+
return int(sum(f[n]) % MOD)
125+
```
126+
* 时间复杂度:$O(n \times C^2),ドル其中 $C = 26$ 为字符集大小
127+
* 空间复杂度:$O(n \times C)$
128+
129+
---
130+
131+
### 转移优化
132+
133+
根据转移的依赖关系,实现上,我们并不需要真正记录每一个 $f[i][X],ドル而可以直接记录一个总的不同子序列方案数 `ans`
134+
135+
这可以避免每次计算新状态时,都累加前一个 $f[i - 1][X]$ 的值,有效减低时空复杂度。
136+
137+
Java 代码:
138+
```Java
139+
class Solution {
140+
int MOD = (int)1e9+7;
141+
public int distinctSubseqII(String s) {
142+
int n = s.length(), ans = 0;
143+
int[] f = new int[26];
144+
for (int i = 0; i < n; i++) {
145+
int c = s.charAt(i) - 'a', prev = f[c];
146+
f[c] = (ans + 1) % MOD;
147+
ans = (ans + f[c]) % MOD;
148+
ans = (ans - prev + MOD) % MOD;
149+
}
150+
return ans;
151+
}
152+
}
153+
```
154+
TypeScript 代码:
155+
```TypeScript
156+
function distinctSubseqII(s: string): number {
157+
const MOD = 1e9+7
158+
let n = s.length, ans = 0
159+
const f = new Array<number>(26).fill(0)
160+
for (let i = 0; i < n; i++) {
161+
const c = s.charCodeAt(i) - 'a'.charCodeAt(0), prev = f[c]
162+
f[c] = (ans + 1) % MOD
163+
ans = (ans + f[c]) % MOD
164+
ans = (ans - prev + MOD) % MOD
165+
}
166+
return ans
167+
}
168+
```
169+
Python 代码:
170+
```Python
171+
class Solution:
172+
def distinctSubseqII(self, s: str) -> int:
173+
n, MOD, ans = len(s), 1e9+7, 0
174+
f = [0] * 26
175+
for i in range(n):
176+
c = ord(s[i]) - ord('a')
177+
prev = f[c]
178+
f[c] = (ans + 1) % MOD
179+
ans = (ans + f[c] - prev) % MOD
180+
return int(ans)
181+
```
182+
* 时间复杂度:$O(n)$
183+
* 空间复杂度:$O(C)$
184+
185+
---
186+
187+
### 最后
188+
189+
这是我们「刷穿 LeetCode」系列文章的第 `No.940` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
190+
191+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
192+
193+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode
194+
195+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
196+

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /