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 1185350

Browse files
✨feat: Add 472
1 parent b470da3 commit 1185350

File tree

3 files changed

+123
-0
lines changed

3 files changed

+123
-0
lines changed

‎Index/字符串哈希.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
| 题目 | 题解 | 难度 | 推荐指数 |
22
| ------------------------------------------------------------ | ------------------------------------------------------------ | ---- | -------- |
33
| [187. 重复的DNA序列](https://leetcode-cn.com/problems/repeated-dna-sequences/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/repeated-dna-sequences/solution/gong-shui-san-xie-yi-ti-shuang-jie-hua-d-30pg/) | 中等 | 🤩🤩🤩🤩 |
4+
| [472. 连接词](https://leetcode-cn.com/problems/concatenated-words/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/concatenated-words/solution/gong-shui-san-xie-xu-lie-dpzi-fu-chuan-h-p7no/) | 困难 | 🤩🤩🤩🤩 |
45
| [686. 重复叠加字符串匹配](https://leetcode-cn.com/problems/repeated-string-match/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/repeated-string-match/solution/gong-shui-san-xie-yi-ti-san-jie-qia-chan-3hbr/) | 中等 | 🤩🤩🤩🤩 |
56
| [1044. 最长重复子串](https://leetcode-cn.com/problems/longest-duplicate-substring/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/longest-duplicate-substring/solution/gong-shui-san-xie-zi-fu-chuan-ha-xi-ying-hae9/) | 困难 | 🤩🤩🤩🤩 |
67

‎Index/序列 DP.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
| [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/) | 困难 | 🤩🤩🤩🤩🤩 |
44
| [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/) | 中等 | 🤩🤩🤩🤩 |
55
| [446. 等差数列划分 II - 子序列](https://leetcode-cn.com/problems/arithmetic-slices-ii-subsequence/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/arithmetic-slices-ii-subsequence/solution/gong-shui-san-xie-xiang-jie-ru-he-fen-xi-ykvk/) | 困难 | 🤩🤩🤩🤩🤩 |
6+
| [472. 连接词](https://leetcode-cn.com/problems/concatenated-words/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/concatenated-words/solution/gong-shui-san-xie-xu-lie-dpzi-fu-chuan-h-p7no/) | 困难 | 🤩🤩🤩🤩 |
67
| [583. 两个字符串的删除操作](https://leetcode-cn.com/problems/delete-operation-for-two-strings/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/delete-operation-for-two-strings/solution/gong-shui-san-xie-cong-liang-chong-xu-li-wqv7/) | 中等 | 🤩🤩🤩🤩 |
78
| [629. K个逆序对数组](https://leetcode-cn.com/problems/k-inverse-pairs-array/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/k-inverse-pairs-array/solution/gong-shui-san-xie-yi-dao-xu-lie-dp-zhuan-tm01/) | 中等 | 🤩🤩🤩🤩🤩 |
89
| [673. 最长递增子序列的个数](https://leetcode-cn.com/problems/number-of-longest-increasing-subsequence/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/number-of-longest-increasing-subsequence/solution/gong-shui-san-xie-lis-de-fang-an-shu-wen-obuz/) | 中等 | 🤩🤩🤩🤩 |
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[472. 连接词](https://leetcode-cn.com/problems/concatenated-words/solution/gong-shui-san-xie-xu-lie-dpzi-fu-chuan-h-p7no/)** ,难度为 **困难**
4+
5+
Tag : 「字符串哈希」、「序列 DP」
6+
7+
8+
9+
10+
给你一个 不含重复 单词的字符串数组 `words` ,请你找出并返回 `words` 中的所有 连接词 。
11+
12+
连接词 定义为:一个完全由给定数组中的至少两个较短单词组成的字符串。
13+
14+
示例 1:
15+
```
16+
输入:words = ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"]
17+
18+
输出:["catsdogcats","dogcatsdog","ratcatdogcat"]
19+
20+
解释:"catsdogcats" 由 "cats", "dog" 和 "cats" 组成;
21+
"dogcatsdog" 由 "dog", "cats" 和 "dog" 组成;
22+
"ratcatdogcat" 由 "rat", "cat", "dog" 和 "cat" 组成。
23+
```
24+
示例 2:
25+
```
26+
输入:words = ["cat","dog","catdog"]
27+
28+
输出:["catdog"]
29+
```
30+
31+
提示:
32+
* 1ドル <= words.length <= 10^4$
33+
* 0ドル <= words[i].length <= 1000$
34+
* $words[i]$ 仅由小写字母组成
35+
* 0ドル <= sum(words[i].length) <= 10^5$
36+
37+
---
38+
39+
### 序列 DP + 字符串哈希
40+
41+
给定数组 $words,ドル先考虑如何判断某个 $s = words[i]$ 是否为「连接词」。
42+
43+
为了方便,我们称组成 `s` 的每个连接部分为 `item`
44+
45+
举个 🌰,例如 `s = abc`,其可能的 `item` 组合为 `a``bc`
46+
47+
判断单个字符串是否为连接词可使用动态规划求解:**定义 $f[i]$ 为考虑 `s` 的前 $i$ 个字符(令下标从 1ドル$ 开始),能够切分出的最大 `item` 数的个数。**
48+
49+
这里之所以采用「记录 $f[i]$ 为最大分割 `item` 数(`int` 类型动规数组)」,而不是「记录 $f[i]$ 为是否可由多个 `item` 组成(`bool` 类型动规数组)」,是因为每个 $s = words[i]$ 至少可由自身组成,采用 `bool` 记录状态的话,最终 $f[n]$ 必然为 `True`,需要额外处理最后一个状态,干脆记录最大分割数量好了。此时如果 `s` 为「连接词」必然有 $f[n] > 1$。
50+
51+
不失一般性的考虑 $f[i]$ 该如何转移:**假设 $f[i]$ 可由 $f[j]$ 转移而来(其中 $j < i$),那么能够转移的充要条件为 $f[j] != 0$ 且子串 $s[(j + 1)..i]$ 在 $words$ 出现过**
52+
53+
其中枚举 $i$ 和 $j$ 的复杂度已经去到 $O(n^2)$ 了,如果常规通过 `HashMap` 等数据结构判断某个字符串是否存在,执行哈希函数时需要对字符进行遍历,整体复杂度去到了 $O(n^3),ドル会 `TLE`
54+
55+
**我们通过「字符串哈希」方式来优化判断某个子串是否存在于 $words$ 中。**
56+
57+
具体的,在判断每个 $s = words[i]$ 是否为为连接词前,先对 $words$ 进行遍历,预处理每个 $words[i]$ 的哈希值,并存入 `HashSet` 中,这样我们将「判断某个子串是否存在于 $words$」的问题转化为「判断某个数值是否存在于 `Set` 当中」。
58+
59+
又由于 **我们在计算某个子串 `s` 的哈希值时,是从前往后处理每一位的 $s[i],ドル因此在转移 $f[i]$ 时,我们期望能够从前往后处理子串,这是常规的从 $[0, i - 1]$ 范围内找可转移点 $f[j]$ 无法做到的**
60+
61+
所以 **我们调整转移逻辑为:从 $f[i]$ 出发,枚举范围 $[i + 1, n],ドル找到可由 $f[i]$ 所能更新的状态 $f[j],ドル并尝试使用 $f[i]$ 来更新 $f[j]$。转移方程为:**
62+
63+
$$
64+
f[j] = \max(f[j], f[i] + 1)
65+
$$
66+
67+
当然,能够转移的前提条件为 $f[i]$ 为有效值,且子串 $s[(i + 1), j]$ 在 $words$ 出现过。
68+
69+
> 一些细节:为了方便,我们定义 $f[i] = -1$ 为无效状态;
70+
另外由于字符串哈希会产生哈希碰撞,这里在计算哈希值的时候,修改了一下哈希计算方式(额外增加了一个 `OFFSET`),当时的目的是想在电脑没电前 `AC`,而另一个更加稳妥的方式是使用双哈希,或是干脆记录某个哈希值对应了哪些字符串。
71+
72+
代码:
73+
```Java
74+
class Solution {
75+
Set<Long> set = new HashSet<>();
76+
int P = 131, OFFSET = 128;
77+
public List<String> findAllConcatenatedWordsInADict(String[] words) {
78+
for (String s : words) {
79+
long hash = 0;
80+
for (char c : s.toCharArray()) hash = hash * P + (c - 'a') + OFFSET;
81+
set.add(hash);
82+
}
83+
List<String> ans = new ArrayList<>();
84+
for (String s : words) {
85+
if (check(s)) ans.add(s);
86+
}
87+
return ans;
88+
}
89+
boolean check(String s) {
90+
int n = s.length();
91+
int[] f = new int[n + 1];
92+
Arrays.fill(f, -1);
93+
f[0] = 0;
94+
for (int i = 0; i <= n; i++) {
95+
if (f[i] == -1) continue;
96+
long cur = 0;
97+
for (int j = i + 1; j <= n; j++) {
98+
cur = cur * P + (s.charAt(j - 1) - 'a') + OFFSET;
99+
if (set.contains(cur)) f[j] = Math.max(f[j], f[i] + 1);
100+
}
101+
if (f[n] > 1) return true;
102+
}
103+
return false;
104+
}
105+
}
106+
```
107+
* 时间复杂度:令 $n$ 为 $words$ 数组长度,$N = \sum_{i = 0}^{n - 1}words[i].length,ドル根据数据范围 $N$ 最大为 1ドルe5$。预处理出 `Set` 的复杂度为 $O(N)$;会对所有 $words[i]$ 执行 `check` 操作,复杂度为 $O((words[i].length)^2),ドル总的计算量最大值为 $O(N^2),ドル由于存在剪枝,实际上达不到该计算量
108+
* 空间复杂度:$O(n + \max(words[i].length))$
109+
110+
---
111+
112+
### 最后
113+
114+
这是我们「刷穿 LeetCode」系列文章的第 `No.472` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
115+
116+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
117+
118+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode。
119+
120+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
121+

0 commit comments

Comments
(0)

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