Skip to content

Commit 5d280e6

Browse files
Merge pull request SharingSource#789 from SharingSource/ac_oier
🌟 feat: add 1032、1129、139、1441、1620、1652、1691、1697、1760、1780、1879、654…
2 parents 9296bfe + 64a6496 commit 5d280e6

File tree

30 files changed

+1807
-463
lines changed

30 files changed

+1807
-463
lines changed

LeetCode/1031-1040/1032. 字符流(困难).md

+85-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ streamChecker.query("l"); // 返回 True ,因为 'kl' 在 words 中
6161

6262
> 一些细节:为了避免每个样例都 `new` 大数组,我们可以使用 `static` 优化。
6363
64-
代码:
64+
Java 代码:
6565
```Java
6666
class StreamChecker {
6767
static int N = 2010 * 200, idx = 0;
@@ -104,6 +104,48 @@ class StreamChecker {
104104
}
105105
}
106106
```
107+
C++ 代码:
108+
```C++
109+
class StreamChecker {
110+
public:
111+
static const int N = 2010 * 200;
112+
int tr[N][26];
113+
bool isEnd[N];
114+
string sb;
115+
int idx = 0;
116+
void add(const string &s) {
117+
int p = 0;
118+
for (char c : s) {
119+
int u = c - 'a';
120+
if (tr[p][u] == 0) tr[p][u] = ++idx;
121+
p = tr[p][u];
122+
}
123+
isEnd[p] = true;
124+
}
125+
bool query(int start, int end) {
126+
int p = 0;
127+
for (int i = start; i <= end; i++) {
128+
int u = sb[i] - 'a';
129+
if (tr[p][u] == 0) return false;
130+
p = tr[p][u];
131+
}
132+
return isEnd[p];
133+
}
134+
StreamChecker(const vector<string>& words) {
135+
memset(tr, 0, sizeof(tr));
136+
memset(isEnd, 0, sizeof(isEnd));
137+
for (const string &s : words) add(s);
138+
}
139+
bool query(char c) {
140+
sb.push_back(c);
141+
int n = sb.length(), min = max(0, n - 200);
142+
for (int i = n - 1; i >= min; i--) {
143+
if (query(i, n - 1)) return true;
144+
}
145+
return false;
146+
}
147+
};
148+
```
107149
* 时间复杂度:`StreamChecker` 初始化复杂度为 $O(n)$,其中 $n$ 为 `words` 字符总数;`query` 操作复杂度为 $O(m^2)$,其中 $m = 200$ 为最大 `words[i]` 长度
108150
* 空间复杂度:$O(n \times C)$,其中 $n$ 为 `words` 字符总数,$C = 26$ 为字符集大小
109151
@@ -123,7 +165,7 @@ class StreamChecker {
123165
124166
同理,我们可以利用最大的 `words[i]` 长度为 $200$ 来控制从 $s[n - 1]$ 开始往回找的最远距离,同时利用当某个短后缀不在 `Trie` 中,则其余长度更大的后缀必然不在 `Trie` 中进行剪枝操作。
125167
126-
代码:
168+
Java 代码:
127169
```Java
128170
class StreamChecker {
129171
static int N = 2010 * 200, idx = 0;
@@ -160,6 +202,47 @@ class StreamChecker {
160202
}
161203
}
162204
```
205+
C++ 代码:
206+
```C++
207+
class StreamChecker {
208+
public:
209+
static const int N = 2010 * 200;
210+
static const int ALPHABET_SIZE = 26;
211+
vector<vector<int>> tr;
212+
vector<bool> isEnd;
213+
string sb;
214+
int idx = 0;
215+
void add(const string &s) {
216+
int p = 0;
217+
for (int i = s.length() - 1; i >= 0; i--) {
218+
int u = s[i] - 'a';
219+
if (tr[p].size() <= u) tr[p].resize(u + 1, 0);
220+
if (tr[p][u] == 0) tr[p][u] = ++idx;
221+
p = tr[p][u];
222+
}
223+
isEnd[p] = true;
224+
}
225+
StreamChecker(const vector<string>& words) {
226+
tr.resize(N);
227+
isEnd.resize(N);
228+
fill(isEnd.begin(), isEnd.end(), false);
229+
for (const auto &s : words) {
230+
add(s);
231+
}
232+
}
233+
bool query(char c) {
234+
sb.push_back(c);
235+
int n = sb.length(), min = max(0, n - 200), p = 0;
236+
for (int i = n - 1; i >= min; i--) {
237+
if (isEnd[p]) return true;
238+
int u = sb[i] - 'a';
239+
if (tr[p].size() <= u || tr[p][u] == 0) return false;
240+
p = tr[p][u];
241+
}
242+
return isEnd[p];
243+
}
244+
};
245+
```
163246
* 时间复杂度:`StreamChecker` 初始化复杂度为 $O(n)$,其中 $n$ 为 `words` 字符总数;`query` 操作复杂度为 $O(m)$,其中 $m = 200$ 为最大 `words[i]` 长度
164247
* 空间复杂度:$O(n \times C)$,其中 $n$ 为 `words` 字符总数,$C = 26$ 为字符集大小
165248

LeetCode/1221-1230/1129. 颜色交替的最短路径(中等).md

+99-6
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,17 @@ Tag : 「BFS」、「最短路」
66

77

88

9-
在一个有向图中,节点分别标记为 `0, 1, ..., n-1`图中每条边为红色或者蓝色,且存在自环或平行边。
9+
在一个有向图中,节点分别标记为 `0, 1, ..., n-1`
1010

11-
`red_edges` 中的每一个 `[i, j]` 对表示从节点 `i` 到节点 `j` 的红色有向边。类似地,`blue_edges` 中的每一个 `[i, j]` 对表示从节点 `i` 到节点 `j` 的蓝色有向边
11+
图中每条边为红色或者蓝色,且存在自环或平行边
1212

13-
返回长度为 `n` 的数组 `answer`,其中 `answer[X]` 是从节点 `0` 到节点 `X` 的红色边和蓝色边交替出现的最短路径的长度。如果不存在这样的路径,那么 `answer[x] = -1`
13+
`red_edges` 中的每一个 `[i, j]` 对表示从节点 `i` 到节点 `j` 的红色有向边。
14+
15+
类似地,`blue_edges` 中的每一个 `[i, j]` 对表示从节点 `i` 到节点 `j` 的蓝色有向边。
16+
17+
返回长度为 `n` 的数组 `answer`,其中 `answer[X]` 是从节点 `0` 到节点 `X` 的红色边和蓝色边交替出现的最短路径的长度。
18+
19+
如果不存在这样的路径,那么 `answer[x] = -1`
1420

1521
示例 1:
1622
```
@@ -70,7 +76,7 @@ Tag : 「BFS」、「最短路」
7076

7177
利用数据范围不大(点数为 $100$,边数为 $800$),可以直接对每个点进行一次独立的 `BFS` 来求最短路。由于图存在重边和自环,因此在求解最短路的过程需要对边进行去重。
7278

73-
代码:
79+
Java 代码:
7480
```Java
7581
class Solution {
7682
static int N = 110, M = 810, idx = 0;
@@ -115,6 +121,54 @@ class Solution {
115121
}
116122
}
117123
```
124+
C++ 代码:
125+
```C++
126+
class Solution {
127+
public:
128+
int he[110], e[810], ne[810], w[810], idx = 0;
129+
void add(int a, int b, int c) {
130+
e[idx] = b;
131+
w[idx] = c;
132+
ne[idx] = he[a];
133+
he[a] = idx++;
134+
}
135+
vector<int> shortestAlternatingPaths(int n, vector<vector<int>>& rs, vector<vector<int>>& bs) {
136+
idx = 0;
137+
fill(he, he + n, -1);
138+
for (auto& e : rs) add(e[0], e[1], 1);
139+
for (auto& e : bs) add(e[0], e[1], -1);
140+
vector<int> ans(n);
141+
vector<bool> vis(rs.size() + bs.size());
142+
for (int k = 1; k < n; k++) {
143+
ans[k] = -1;
144+
fill(vis.begin(), vis.end(), false);
145+
queue<vector<int>> q;
146+
q.push({ 0, 0, 0 }); // point, last, step
147+
bool out = false;
148+
while (!q.empty()) {
149+
if (out) break;
150+
vector<int> info = q.front(); q.pop();
151+
int p = info[0], last = info[1], step = info[2];
152+
for (int i = he[p]; i != -1; i = ne[i]) {
153+
int j = e[i], c = w[i];
154+
if (vis[i]) continue;
155+
if (c + last == 0 || last == 0) {
156+
if (j == k) {
157+
ans[k] = step + 1;
158+
out = true;
159+
break;
160+
} else {
161+
q.push({ j, c, step + 1 });
162+
vis[i] = true;
163+
}
164+
}
165+
}
166+
}
167+
}
168+
return ans;
169+
}
170+
};
171+
```
118172
* 时间复杂度:将点数记为 `n` ,边数记为 `m`。建图复杂度为 $O(m)$;构造答案复杂度为 $O(n \times (n + m))$。整体复杂度为 $O(n \times (n + m))$
119173
* 空间复杂度:$O(n + m)$
120174
@@ -128,7 +182,7 @@ class Solution {
128182
129183
因此我们只需要初始化所有的 `ans[i] = -1`,并从节点 `0` 开始进行一次 `BFS` 即可。
130184
131-
代码:
185+
Java 代码:
132186
```Java
133187
class Solution {
134188
static int N = 110, M = 810, idx = 0;
@@ -168,14 +222,53 @@ class Solution {
168222
}
169223
}
170224
```
225+
C++ 代码:
226+
```C++
227+
class Solution {
228+
public:
229+
int he[110], e[810], ne[810], w[810], idx = 0;
230+
void add(int a, int b, int c) {
231+
e[idx] = b;
232+
w[idx] = c;
233+
ne[idx] = he[a];
234+
he[a] = idx++;
235+
}
236+
vector<int> shortestAlternatingPaths(int n, vector<vector<int>>& rs, vector<vector<int>>& bs) {
237+
idx = 0;
238+
fill(he, he + n, -1);
239+
for (auto& e : rs) add(e[0], e[1], 1);
240+
for (auto& e : bs) add(e[0], e[1], -1);
241+
vector<int> ans(n, -1);
242+
ans[0] = 0;
243+
vector<bool> vis(rs.size() + bs.size(), false);
244+
deque<vector<int>> d;
245+
d.push_back({0, 0, 0}); // point, last, step
246+
while (!d.empty()) {
247+
vector<int> info = d.front();
248+
d.pop_front();
249+
int p = info[0], last = info[1], step = info[2];
250+
for (int i = he[p]; i != -1; i = ne[i]) {
251+
if (vis[i]) continue;
252+
int j = e[i], c = w[i];
253+
if (c + last == 0 || last == 0) {
254+
ans[j] = ans[j] == -1 ? step + 1 : min(ans[j], step + 1);
255+
d.push_back({j, c, step + 1});
256+
vis[i] = true;
257+
}
258+
}
259+
}
260+
return ans;
261+
}
262+
};
263+
```
171264
* 时间复杂度:将点数记为 `n` ,边数记为 `m`。建图复杂度为 $O(m)$;构造答案复杂度为 $O(n + m)$。整体复杂度为 $O(n + m)$
172265
* 空间复杂度:$O(n + m)$
173266
174267
---
175268
176269
### 最后
177270
178-
这是我们「刷穿 LeetCode」系列文章的第 `No.1224` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
271+
这是我们「刷穿 LeetCode」系列文章的第 `No.1129` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
179272
180273
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
181274

LeetCode/131-140/139. 单词拆分(中等).md

+35-13
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,27 @@ class Solution {
7474
}
7575
}
7676
```
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]
77+
C++ 代码:
78+
```C++
79+
class Solution {
80+
public:
81+
bool wordBreak(string s, vector<string>& wordDict) {
82+
unordered_set<string> set;
83+
for (const string& word : wordDict) {
84+
set.insert(word);
8885
}
86+
int n = s.length();
87+
vector<bool> f(n + 10, false);
88+
f[0] = true;
89+
for (int i = 1; i <= n; ++i) {
90+
for (int j = 1; j <= i && !f[i]; ++j) {
91+
string sub = s.substr(j - 1, i - j + 1);
92+
if (set.find(sub) != set.end()) f[i] = f[j - 1] || f[i];
93+
}
94+
}
95+
return f[n];
8996
}
90-
return f[n]
91-
}
97+
};
9298
```
9399
Python 代码:
94100
```Python
@@ -107,6 +113,22 @@ class Solution:
107113
j -= 1
108114
return f[n]
109115
```
116+
TypeScript 代码:
117+
```TypeScript
118+
function wordBreak(s: string, wordDict: string[]): boolean {
119+
const ss = new Set<string>(wordDict)
120+
const n = s.length
121+
const f = new Array<boolean>(n + 10).fill(false)
122+
f[0] = true
123+
for (let i = 1; i <= n; i++) {
124+
for (let j = i; j >= 1 && !f[i]; j--) {
125+
const sub = s.substring(j - 1, i)
126+
if (ss.has(sub)) f[i] = f[j - 1]
127+
}
128+
}
129+
return f[n]
130+
}
131+
```
110132
* 时间复杂度:将 `wordDict` 转存在 `Set` 复杂度为 $O(m)$;`DP` 过程复忽裁剪子串和查询 `Set` 结构的常数,复杂度为 $O(n^2)$
111133
* 空间复杂度:$O(n + m)$
112134

0 commit comments

Comments
 (0)