# Leetcode 第213场周赛题解
# Problem A - 能否连接形成数组 (opens new window)
因为题目说明了和中所有数字都是互异的,所以这题实际是一个模拟题。我们首先用一个哈希表记录下中每一个数组的第一个数字对应的中的下标,接下来,我们对进行遍历。如果当前位置不在哈希表中,则说明没有合法的起点,直接返回false
。如果当前位置在哈希表中,表明应该使用中对应的那一个数组,检查其是否符合要求即可。
- 时间复杂度=。为的长度。
- 空间复杂度。为的长度。
参考代码(C++)
class Solution {
public:
bool canFormArray(vector<int>& arr, vector<vector<int>>& pieces) {
int n = arr.size(), m = pieces.size();
unordered_map<int, int> mp;
for (int i = 0; i < m; ++i)
mp[pieces[i][0]] = i;
for (int i = 0; i < n; ++i) {
if (!mp.count(arr[i]))
return false;
int j = mp[arr[i]];
for (int k = 1; k < pieces[j].size(); ++k) {
if (arr[i + k] != pieces[j][k])
return false;
}
i += pieces[j].size() - 1;
}
return true;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Problem B - 统计字典序元音字符串的数目 (opens new window)
# 方法一 动态规划
用表示最后一个字母不大于第个元音字母的长度为的字符串数目。显然,我们有转移:
注意到这一转移式实际只需要用一维数组进行存储,我们可以对空间进行优化。
- 时间复杂度,本题中。
- 空间复杂度。
我们也可以用表示最后一个字母恰好为第个元音字母的长度为的字符串数目,但使用这样的表示方式,将使得转移变为:
从而时间复杂度为。
事实上,可以注意到,,也即方法一中的数组实际上恰好是方法二中的数组的前缀和。
参考代码(C++)
class Solution {
public:
int countVowelStrings(int n) {
vector<int> dp(5, 1);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= 4; ++j)
dp[j] += dp[j - 1];
return dp[4];
}
};
2
3
4
5
6
7
8
9
10
# 方法二 组合计数
在本题中,我们可以写出方程:
其中均为非负整数。
不妨令
则我们有:
其中均为正整数。
此时我们可以使用隔板法求解,一共个间隔,需要放入个搁板,所以最后的答案为。
参考代码 (Python 3)
class Solution:
def countVowelStrings(self, n: int) -> int:
return math.comb(n + 4, 4)
2
3
# Problem C - 可以到达的最远建筑 (opens new window)
贪心。我们应当首先使用梯子,如果梯子已经用完,我们需要找出之前最小的一个高度差,改为使用砖块。这提示我们使用优先队列来存储每一个大于的高度差。
如果当前砖块的需求量已经超过了供给量,我们应当提前返回答案。
- 时间复杂度。
- 空间复杂度。
参考代码(C++)
class Solution {
public:
int furthestBuilding(vector<int>& heights, int bricks, int ladders) {
int n = heights.size();
int need = 0;
priority_queue<int, vector<int>, greater<>> pq;
for (int i = 0; i + 1 < n; ++i) {
int delta = heights[i + 1] - heights[i];
if (delta <= 0)
continue;
pq.push(delta);
if (pq.size() > ladders) {
int small = pq.top();
pq.pop();
need += small;
if (need > bricks)
return i;
}
}
return n - 1;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Problem D - 第 K 条最小指令 (opens new window)
不管我们如何行走,最后的指令中总是包含个V
和个H
,所以,我们实际上就是要求,含有个V
和个H
的字符串中,字典序第小的那一个。
不妨先设当前的字典序为。每一步我们有两种选择:H
或V
(如果某一字母已经用完,则只有一种选择)。如果选择H
,那么字典序不会增加;但如果选择V
,则需要计入所有以H
开头的字符串的数目,这是字典序增加的最小数目。以H
开头的字符串的数目可以通过组合求解,假设当前剩余个H
和个V
,则以H
开头的字符串的数目为。我们计算出这一数目后,将当前字典序加上这一数字,如果超过了,则表明我们必须选择H
;否则我们必须选择V
。
- 时间复杂度。
- 空间复杂度。
参考代码(Python 3)
class Solution:
def kthSmallestPath(self, destination: List[int], k: int) -> str:
ans = []
v, h = destination
fac = [1]
for i in range(v + h):
fac.append(fac[-1] * (i + 1))
def comb(n, k):
return fac[n] // fac[n - k] // fac[k];
L = 1
for i in range(v + h):
if v == 0:
ans.append('H')
continue
if h == 0:
ans.append('V')
continue
left = comb(v + h - 1, h - 1)
if L + left > k:
ans.append('H')
h -= 1
else:
ans.append('V')
L += left
v -= 1
return ''.join(ans)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28