Preface
眨眼间Blog停更了3个月,这段时间主要在忙着春招找工作,现在春招也临近尾声,手头的面试也差不多告一段落了。这周刚好闲下来可以重回LC周赛(回归第一场就被锤爆),接下来计划更新我在国内春招和新加坡找工的笔试面试经验,以及每周会更新LC周赛题解。ヾ(•ω•`)o
Summary
个人觉得这周的题质量还蛮高的,T2, T3, T4都比较贴近面试水平。
Solution
字符串中最大的 3 位相同数字
按照题意把连续相同的3位数字抠出来然后比较即可。
Based on the description, we can slice out all the 3-digit substring with the same number and compare it with the maximum.
Code Q1
class Solution {
public:
string largestGoodInteger(string s) {
string res;
for (int i = 0, j = 0; i < s.size(); i++) {
if (!i || s[i] == s[i - 1]) j++;
else j = 1;
if (j == 3) {
res = max(res, s.substr(i - 2, 3));
}
}
return res;
}
};
统计值等于子树平均值的节点数
通过深度优先搜索可以维护出每个节点的子孙节点的总数与值之和,对于每个点判断是否满足条件即可。
For each node in the tree, we can maintain the number of its children and grand-children nodes, and the sum of their values. Then we can check whether they satisfy the requirement. We can use depth-first-search to implement this process.
Code Q2
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int res;
int averageOfSubtree(TreeNode* root) {
res = 0;
dfs(root);
return res;
}
vector<int> dfs(TreeNode* root) {
if (!root) return {0, 0};
vector<int> left = dfs(root->left);
vector<int> right = dfs(root->right);
if ( (left[0] + right[0] + root->val) / (left[1] + right[1] + 1) == root->val) res++;
return {left[0] + right[0] + root->val, left[1] + right[1] + 1};
}
};
统计打字方案数
本题的关键在于挖掘出递推关系,本质上和爬楼梯问题相同,但不容易看出递推关系。
The key to solving this problem is finding the recursion relation. In essence, it is the same as the problem of climbing stairs, but a bit difficult to find the relation.
可以将输入字符串拆解成若干段,这些子串由连续相同的字母组成。由于按键不可能“跨键”组合,所以每一段是独立的,我们只需要算每一段的方案数,然后根据乘法原理累乘即可得出最终结果。
We can split the string into several segments, which are made of continuous same characters. Given that different characters can not be merged, each segment can be regarded as a independent sub-problem. What we need to do is to calculate the number of cases from each segment, then get the final result by multiplying them up.
不妨设$f(i)$为具有连续$i$个相同字母的字符串的方案数。对于非7,9数字来说,可以将末尾的1个字母固定,剩下的$i - 1$个字母构成$f(i - 1)$的子问题。同理,可以将末尾2个字母合成一个(比如2个a可以构成b),剩下的$i - 2$个字母构成$f(i - 2)$。3个字母类似不再赘述。因此,$f(i)$可以由以下式子递推得到:
$$f(i) = f(i-1)+f(i-2)+f(i-3)$$
对于7和9,由于它们可以将4个相同字母合成一个,所以递推式应该增加一项:
$$g(i) = g(i-1)+g(i-2)+g(i-3)+g(i-4)$$
初始化时,$f(1)=1,f(2)=2,f(3)=4$; 特别地,我们定义$g(0)=1$,这样可以正确推出$g(4)$。
Let $f(i)$ be the number of case(s) of a segment with continuous same $i$ characters. For those number except for 7 and 9, we can fix the last character, the remaining characters form a sub-problem $f(i - 1)$. Similarly, we can fix the last two characters and merge them, the remaining characters form a sub-problem $f(i - 2)$. The same for 3 characters. Therefore, $f(i)$ can be derived through the following relation:
$$f(i) = f(i-1)+f(i-2)+f(i-3)$$
For 7 and 9, since they can merge 4 characters into 1, an extra item should be added to the recursion formula:
$$g(i) = g(i-1)+g(i-2)+g(i-3)+g(i-4)$$
For the initial step, $f(1)=1,f(2)=2,f(3)=4$; specially, we let $g(0)=1$ in order to correctly derive $g(4)$.
Code Q3
class Solution {
public:
const int MOD = 1e9 + 7, N = 1e5;
using LL = long long;
int countTexts(string s) {
int n = s.size();
vector<LL> f(N + 1), g(N + 1);
g[0] = 1;
f[1] = g[1] = 1;
f[2] = g[2] = 2;
f[3] = g[3] = 4;
for (int i = 4; i <= N; i++) {
f[i] = (f[i - 1] + f[i - 2] + f[i - 3]) % MOD;
g[i] = (g[i - 1] + g[i - 2] + g[i - 3] + g[i - 4]) % MOD;
}
LL res = 1;
for (int i = 0; i < n; i++) {
int j = i + 1;
while (j < n && s[j - 1] == s[j]) j++;
if (s[i] == '7' || s[i] == '9') res = (res * g[j - i]) % MOD;
else res = (res * f[j - i]) % MOD;
i = j - 1;
}
return res;
}
};
检查是否有合法括号字符串路径
本题的一维版本十分简单,直接遍历括号序列并判断合法性即可。
二维版本由于每个位置可以有2种选择,注意到$n$的值还是比较大的,直接暴搜会超时。考虑在暴搜的同时加入必要的剪枝操作:
- 如果左括号数量超过了$\frac{(n+m)}{2}$并且已经走过$\frac{(n+m)}{2}$的路程,那么剩下的即使全是右括号也无法把左括号匹配掉;
- 记忆化搜索。定义状态$f(x,y,c)$表示从$(0,0)$到$(x,y)$有$c$个左括号的所有路径的集合。因为我们最终只需判断是否可达,所以只要有一条路径能达到状态$f(x,y,c)$即可。剩下的路径不需要搜索。
The 1-D version of this question is simple, we can iterate through the parenthese sequence and check the validity.
For 2-D version, since each position we have 2 choices, it is likely to TLE due to $n=100$, which is big. Therefore, we can consider to add pruning in the searching tree:
- If we have moved more than $\frac{(n+m)}{2}$ steps with more than $\frac{(n+m)}{2}$ left parentheses in hand, we can just stop searching because even though the remaining parentheses are all right parentheses, we still cannot match all of the left parentheses.
- Memorized searching. Define $f(x,y,c)$ as a set of paths which are from $(0,0)$ to $(x,y)$ with $c$ left parentheses. Given that our goal is to check whether we can reach the target legally, once the state $f(x,y,c)$ is reach, we don’t need further duplicate search for this state, thus reducing the time complexity.
Code Q4
class Solution {
public:
vector<vector<char>> g;
int n, m;
vector<vector<vector<bool>>> vis;
bool hasValidPath(vector<vector<char>>& grid) {
g = grid;
n = g.size(), m = g[0].size();
vis = vector<vector<vector<bool>>>(n, vector<vector<bool>>(m, vector<bool>(m + n)));
return dfs(0, 0, 0);
}
bool dfs(int x, int y, int u) {
if (x >= n || y >= m) return false;
if (u > m - x + n - y - 1) return false;
if (vis[x][y][u]) return false;
vis[x][y][u] = true;
if (g[x][y] == '(') u++;
else u--;
if (x == g.size() - 1 && y == g[0].size() - 1) {
if (!u) return true;
return false;
}
return u >= 0 && (dfs(x + 1, y, u) || dfs(x, y + 1, u));
}
};
- Post link: https://scnujackychen.github.io/2022/05/08/LC-weekly-contest-292/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.
若没有本文 Issue,您可以使用 Comment 模版新建。
GitHub IssuesGitHub Discussions