LeetCode Weekly 289 题解
Problem A: 计算字符串的数字和
按照题目意思模拟即可,题目主要操作为:
- 将数组切分成长度为 k 的若干段,利用 Python 切片功能显示
- 对数字统计各位之和,定义 sum 函数实现制定功能
- 重复题目所述操作直到满足条件,利用递归实现
class Solution:
def sum(self, s):
ret = 0
for c in s: ret += int(c)
return ret
def digitSum(self, s: str, k: int) -> str:
if len(s) <= k: return s
n = len(s)
strs = [s[i: min(i+k, n)] for i in range(0, n, k)]
ret = ""
for s in strs:
ret += str(self.sum(s))
return self.digitSum(ret, k)
Problem B: 完成所有任务需要的最少轮数
仅难度相同的任务才可以一起完成,因此,首先对任务数组排序,将其切分成任务难度相同的若干段,对每一段单独考虑。
考虑一段长度为 \(l\) 的任务,由于每次仅能完成 \(2\) 个或 \(3\) 个任务,因此当 \(l\) 为 \(1\) 时,永远无法完成它,直接返回不可行,否则一定可以通过 \(\lfloor \frac{l}{3} \rfloor\) 轮将这段任务完成。
class Solution {
public:
int minimumRounds(vector<int>& tasks) {
sort(tasks.begin(), tasks.end());
int n = tasks.size();
int ans = 0;
for (int i = 0, j; i < n; i = j){
j = i;
while(j < n && tasks[j] == tasks[i]) j++;
int l = j - i;
if (l == 1) return -1;
if (l % 3 == 0) ans += l / 3; else ans += l / 3 + 1;
}
return ans;
}
};
Problem C: 转角路径的乘积中最多能有几个尾随零
由于我们仅需要计算乘积末尾 \(0\) 的数量,因此,我们只需要统计乘积中质因子 \(2\) 与 \(5\) 的数量即可。
由题意,拐角至多出现一次,因此,我们可以首先枚举拐角处的元素,然后枚举从此点出发的路径分别往什么方向延伸,复杂度为 \(O(4nm)=O(nm)\)。
对于每种的情况,我们需要快速计算乘积中 \(2\) 与 \(5\) 的数量,这里用前缀和就可以做到。在代码中,我们首先维护纵方向与横方向数字中质因子 \(2\) 与 \(5\) 数量的前缀和,在查询时,利用两个前缀和相减即可统计出一段路径上对应的质因子数量。
const int MAXN = 5e5 + 50;
int sav2[2][MAXN], sav5[2][MAXN];
class Solution {
public:
void set(int arr[], int m, int i, int j, int v){
m += 1;
arr[i * m + j] = v;
}
int get(int arr[], int m, int i, int j){
m += 1;
return arr[i * m + j];
}
int gets(int arr[], int n, int m, int i, int j, int typ, int dir){
if (dir == 0) return get(arr, m, i, j);
if (typ == 0) return get(arr, m, n, j) - get(arr, m, i - 1, j);
return get(arr, m, i, m) - get(arr, m, i, j - 1);
}
int maxTrailingZeros(vector<vector<int>>& grid) {
int n = grid.size(), m = grid[0].size();
for (int i = 0; i <= n; i++) set(sav2[0], m, i, 0, 0), set(sav2[1], m, i, 0, 0), set(sav5[0], m, i, 0, 0), set(sav5[1], m, i, 0, 0);
for (int j = 0; j <= m; j++) set(sav2[0], m, 0, j, 0), set(sav2[1], m, 0, j, 0), set(sav5[0], m, 0, j, 0), set(sav5[1], m, 0, j, 0);
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++){
int cnt2 = 0, cnt5 = 0;
while(grid[i][j] % 2 == 0) cnt2 += 1, grid[i][j] /= 2;
while(grid[i][j] % 5 == 0) cnt5 += 1, grid[i][j] /= 5;
set(sav2[0], m, i + 1, j + 1, get(sav2[0], m, i, j + 1) + cnt2);
set(sav2[1], m, i + 1, j + 1, get(sav2[1], m, i + 1, j) + cnt2);
set(sav5[0], m, i + 1, j + 1, get(sav5[0], m, i, j + 1) + cnt5);
set(sav5[1], m, i + 1, j + 1, get(sav5[1], m, i + 1, j) + cnt5);
}
}
int ans = 0;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
for (int ta = 0; ta < 2; ta++) for (int tb = 0; tb < 2; tb++)
ans = max(ans, min(
gets(sav2[0], n, m, i, j, 0, ta)
+ gets(sav2[1], n, m, i, j, 1, tb)
- (get(sav2[0], m, i, j) - get(sav2[0], m, i - 1, j)),
gets(sav5[0], n, m, i, j, 0, ta)
+ gets(sav5[1], n, m, i, j, 1, tb)
- (get(sav5[0], m, i, j) - get(sav5[0], m, i - 1, j))
));
}
}
return ans;
}
};
Problem D: 6073. 相邻字符不同的最长路径
简单地在树上进行动态规划。
首先考虑树上的一条路径,找到其中深度为小的节点,记为 \(x\)。此路径可以拆解为 \(x\) 向下的两条链(也有可能是一条)。因此,想要找到最长路径,不妨在树上 DFS 到节点 \(x\) 时候考虑所有以 \(x\) 为根结点向下延伸两条链组成的路径,当我们 DFS 了整棵树,也就考虑了全部的路径。
当我们 DFS 到节点 \(x\) 的时候,我们需要知道从 \(x\) 向下延伸且相邻字符不同的链中长度最大与次大是多少(将他们拼起来可以形成长度最长的候选路径)。因此,定义 DFS 函数返回从 \(x\) 节点向下且相邻字符不同的链的最长长度,并在 DFS 的过程中进行拼接链的操作。
整体复杂度为 \(O(n)\)。
const int MAXN = 1e5 + 50;
vector<int> edge[MAXN];
class Solution {
public:
int ans = 0;
int dfs(int x, int fa, const string &s){
char c = s[x];
int m1 = 0, m2 = 0;
for (int v: edge[x]){
char nc = s[v];
if (nc == c) dfs(v, x, s); else{
int m = dfs(v, x, s);
if (m > m1) m2 = m1, m1 = m; else{
if (m > m2) m2 = m;
}
}
}
ans = max(ans, m1 + m2 + 1);
return m1 + 1;
}
int longestPath(vector<int>& fa, string s) {
int n = fa.size(), rt = -1;
for (int i = 0; i < n; i++) edge[i].clear();
for (int i = 0; i < n; i++){
if (fa[i] == -1) rt = i; else {
edge[fa[i]].push_back(i);
}
}
ans = 0;
dfs(rt, -1, s);
return ans;
}
};

原文链接:LeetCode Weekly 289 题解
WNJXYKの博客 版权所有,转载请注明出处。
评论功能已经关闭!