Skip to content

Commit b7d4d81

Browse files
committed
✨feat: add 139
1 parent 85e83d0 commit b7d4d81

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

Index/哈希表.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
| [36. 有效的数独](https://leetcode-cn.com/problems/valid-sudoku/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/valid-sudoku/solution/gong-shui-san-xie-yi-ti-san-jie-ha-xi-bi-ssxp/) | 中等 | 🤩🤩🤩🤩 |
88
| [137. 只出现一次的数字 II](https://leetcode-cn.com/problems/single-number-ii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/single-number-ii/solution/gong-shui-san-xie-yi-ti-san-jie-ha-xi-bi-fku8/) | 中等 | 🤩🤩🤩 |
99
| [138. 复制带随机指针的链表](https://leetcode-cn.com/problems/copy-list-with-random-pointer/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/copy-list-with-random-pointer/solution/gong-shui-san-xie-yi-ti-shuang-jie-ha-xi-pqek/) | 中等 | 🤩🤩🤩 |
10+
| [139. 单词拆分](https://leetcode.cn/problems/word-break/) | [LeetCode 题解链接](https://leetcode.cn/problems/word-break/solution/by-ac_oier-gh00/) | 中等 | 🤩🤩🤩 |
1011
| [146. LRU 缓存机制](https://leetcode-cn.com/problems/lru-cache/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/lru-cache/solution/gong-shui-san-xie-she-ji-shu-ju-jie-gou-68hv2/) | 中等 | 🤩🤩🤩🤩🤩 |
1112
| [149. 直线上最多的点数](https://leetcode-cn.com/problems/max-points-on-a-line/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/max-points-on-a-line/solution/gong-shui-san-xie-liang-chong-mei-ju-zhi-u44s/) | 困难 | 🤩🤩🤩 |
1213
| [166. 分数到小数](https://leetcode-cn.com/problems/fraction-to-recurring-decimal/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/fraction-to-recurring-decimal/solution/gong-shui-san-xie-mo-ni-shu-shi-ji-suan-kq8c4/) | 中等 | 🤩🤩🤩🤩 |

Index/序列 DP.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
| 题目 | 题解 | 难度 | 推荐指数 |
22
| ------------------------------------------------------------ | ------------------------------------------------------------ | ---- | -------- |
3+
| [139. 单词拆分](https://leetcode.cn/problems/word-break/) | [LeetCode 题解链接](https://leetcode.cn/problems/word-break/solution/by-ac_oier-gh00/) | 中等 | 🤩🤩🤩🤩🤩 |
34
| [334. 递增的三元子序列](https://leetcode-cn.com/problems/increasing-triplet-subsequence/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/increasing-triplet-subsequence/solution/gong-shui-san-xie-zui-chang-shang-sheng-xa08h/) | 中等 | 🤩🤩🤩🤩 |
45
| [354. 俄罗斯套娃信封问题](https://leetcode-cn.com/problems/russian-doll-envelopes/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/russian-doll-envelopes/solution/zui-chang-shang-sheng-zi-xu-lie-bian-xin-6s8d/) | 困难 | 🤩🤩🤩🤩🤩 |
56
| [368. 最大整除子集](https://leetcode-cn.com/problems/largest-divisible-subset/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/largest-divisible-subset/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-0a3jc/) | 中等 | 🤩🤩🤩🤩 |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[139. 单词拆分](https://leetcode.cn/problems/word-break/solution/by-ac_oier-gh00/)** ,难度为 **中等**
4+
5+
Tag : 「动态规划」、「哈希表」、「序列 DP」
6+
7+
8+
9+
给你一个字符串 `s` 和一个字符串列表 `wordDict` 作为字典。请你判断是否可以利用字典中出现的单词拼接出 `s`
10+
11+
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
12+
13+
示例 1:
14+
```
15+
输入: s = "leetcode", wordDict = ["leet", "code"]
16+
17+
输出: true
18+
19+
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。
20+
```
21+
示例 2:
22+
```
23+
输入: s = "applepenapple", wordDict = ["apple", "pen"]
24+
25+
输出: true
26+
27+
解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。
28+
  注意,你可以重复使用字典中的单词。
29+
```
30+
示例 3:
31+
```
32+
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
33+
34+
输出: false
35+
```
36+
37+
提示:
38+
* $1 <= s.length <= 300$
39+
* $1 <= wordDict.length <= 1000$
40+
* $1 <= wordDict[i].length <= 20$
41+
* `s``wordDict[i]` 仅有小写英文字母组成
42+
* `wordDict` 中的所有字符串 互不相同
43+
44+
---
45+
46+
### 序列 DP
47+
48+
将字符串 `s` 长度记为 $n$,`wordDict` 长度记为 $m$。为了方便,我们调整字符串 `s` 以及将要用到的动规数组的下标从 $1$ 开始。
49+
50+
定义 $f[i]$ 为考虑前 $i$ 个字符,能否使用 `wordDict` 拼凑出来:当 $f[i] = true$ 代表 $s[1...i]$ 能够使用 `wordDict` 所拼凑,反之则不能。
51+
52+
不失一般性考虑 $f[i]$ 该如何转移:由于 $f[i]$ 需要考虑 $s[1...i]$ 范围内的字符,若 $f[i]$ 为 `True` 说明整个 $s[1...i]$ 都能够使用 `wordDict` 拼凑,自然也包括最后一个字符 $s[i]$ 所在字符串 `sub`
53+
54+
**我们可以枚举最后一个字符所在字符串的左端点 $j$,若 $sub = s[j...i]$ 在 `wordDict` 中出现过,并且 $f[j - 1] = True$,说明 $s[0...(j - 1)]$ 能够被拼凑,并且子串 `sub` 也在 `wordDict`,可得 `f[i] = True`**
55+
56+
为了快速判断某个字符是否在 `wordDict` 中出现,我们可以使用 `Set` 结构对 $wordDict[i]$ 进行转存。
57+
58+
Java 代码:
59+
```Java
60+
class Solution {
61+
public boolean wordBreak(String s, List<String> wordDict) {
62+
Set<String> set = new HashSet<>();
63+
for (String word : wordDict) set.add(word);
64+
int n = s.length();
65+
boolean[] f = new boolean[n + 10];
66+
f[0] = true;
67+
for (int i = 1; i <= n; i++) {
68+
for (int j = 1; j <= i && !f[i]; j++) {
69+
String sub = s.substring(j - 1, i);
70+
if (set.contains(sub)) f[i] = f[j - 1];
71+
}
72+
}
73+
return f[n];
74+
}
75+
}
76+
```
77+
TypeScript 代码:
78+
```TypeScript
79+
function wordBreak(s: string, wordDict: string[]): boolean {
80+
const ss = new Set<string>(wordDict)
81+
const n = s.length
82+
const f = new Array<boolean>(n + 10).fill(false)
83+
f[0] = true
84+
for (let i = 1; i <= n; i++) {
85+
for (let j = i; j >= 1 && !f[i]; j--) {
86+
const sub = s.substring(j - 1, i)
87+
if (ss.has(sub)) f[i] = f[j - 1]
88+
}
89+
}
90+
return f[n]
91+
}
92+
```
93+
Python 代码:
94+
```Python
95+
class Solution:
96+
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
97+
ss = set(wordDict)
98+
n = len(s)
99+
f = [False] * (n + 10)
100+
f[0] = True
101+
for i in range(1, n + 1):
102+
j = i
103+
while j >= 1 and not f[i]:
104+
sub = s[j - 1:i]
105+
if sub in ss:
106+
f[i] = f[j - 1]
107+
j -= 1
108+
return f[n]
109+
```
110+
* 时间复杂度:将 `wordDict` 转存在 `Set` 复杂度为 $O(m)$;`DP` 过程复忽裁剪子串和查询 `Set` 结构的常数,复杂度为 $O(n^2)$
111+
* 空间复杂度:$O(n + m)$
112+
113+
---
114+
115+
### 总结
116+
117+
这里简单说下「线性 DP」和「序列 DP」的区别。
118+
119+
线性 DP 通常强调「状态转移所依赖的前驱状态」是由给定数组所提供的,即拓扑序是由原数组直接给出。更大白话来说就是通常有 $f[i][...]$ 依赖于 $f[i - 1][...]$。
120+
121+
这就限定了线性 DP 的复杂度是简单由「状态数量(或者说是维度数)」所决定。
122+
123+
序列 DP 通常需要结合题意来寻找前驱状态,即需要自身寻找拓扑序关系(例如本题,需要自己通过枚举的方式来找左端点,从而找到可转移的前驱状态 $f[j - 1]$)。
124+
125+
这就限定了序列 DP 的复杂度是由「状态数 + 找前驱」的复杂度所共同决定。也直接导致了序列 DP 有很多玩法,往往能够结合其他知识点出题,来优化找前驱这一操作,通常是利用某些性质,或是利用数据结构进行优化。
126+
127+
---
128+
129+
### 最后
130+
131+
这是我们「刷穿 LeetCode」系列文章的第 `No.139` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
132+
133+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
134+
135+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode
136+
137+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
138+

0 commit comments

Comments
 (0)