week31
好家伙上来就这么硬核的吗...
301 - 删除无效的括号
记得复习一下leetcode 32.最长有效括号
有效括号序列要满足的要求:
左右括号数量相同
任意一个前缀中,左括号数量 >= 右括号数量
第一步:统计要删掉多少左括号和右括号
l表示还剩多少未匹配的左括号r表示多少非法的右括号
那么l ++代表当前未匹配的左括号,如果遇到右括号并且当前未匹配的左括号个数大于0,那么l --,说明当前右括号前面有与之匹配的左括号,如果未匹配的左括号个数等于0,说明当前这个括号是不合法的,r ++
最后l代表还没有匹配的左括号个数,r表示非法的右括号个数,都是我们需要删除的个数。
第二步:DFS搜索+剪枝
这里的剪枝策略有两部分:
预处理删除的括号数
重复去重 ((( 对于这种连续的括号序列删除那个都是一样的 所以这里枚举的是删除的个数
函数签名void dfs(string& s, int u, string path, int cnt, int l, int r)
这里面cnt和上面的l含义一致,还有多少未匹配的左括号, l r都是还需要删除的括号数量
class Solution {
public:
vector<string> res;
public:
vector<string> removeInvalidParentheses(string s) {
int l = 0, r = 0;
// 第一步:统计待删除的字符数量
for(auto x : s)
{
if(x == '(') l++;
else if (x == ')')
{
if(l == 0) r++;
else l--;
}
}
dfs(s, 0, "", 0, l, r);
return res;
}
void dfs(string& s, int u, string path, int cnt, int l, int r)
{
if(u == s.size())
{
if(!cnt) res.push_back(path); // 合法条件:左右括号数量一致
return;
}
if(s[u] != '(' && s[u] != ')') dfs(s, u + 1, path + s[u], cnt, l, r);
else if (s[u] == '(')
{
int k = u;
while(k < s.size() && s[k] == '(') k++;
// 按照数量枚举,先把所有的都删了
l -= k - u;
for(int i = k - u; i >= 0; i--)
{
if(l >= 0) dfs(s, k, path, cnt, l, r);
path += '(';
cnt++, l++; //
}
}
else if (s[u] == ')')
{
int k = u;
while(k < s.size() && s[k] == ')') k++;
r -= k - u;
for(int i = k - u; i >= 0; i--)
{
if(cnt >= 0 && r >= 0) dfs(s, k, path, cnt, l, r); // 注意这里的 cnt >= 0
path += ')';
cnt--, r++;
}
}
}
};303 - 区域和检索 - 数组不可变
这题简单 一维前缀和罢了
304 - 二维区域和检索 - 数组不可变
这题就是上一题的推广 二维前缀和
注意的点一个是下标要从0映射到1 另一个是是闭区间 所以row1 和 col1都要减去1
306 - 累加数
判断一个字符串是不是斐波那契数列 但是没有分隔符
用高精度的方式来存每个数
枚举第一个数的长度和第二个数的长度,然后递归判断是不是斐波那契数列, 所以三个数分别是 [a + 1, b] 和 [b + 1, c] 和 [c + 1, c + z.size()], 不断往后指就可以了
时间复杂度:o(n^3) 枚举+判断
不能枚举有前导0的数
307 - 区域和检索 - 数组可修改
能用树状数组就用树状数组,不行再用线段树;
树状数组:代码更短 空间更少 时间更快
所以本题就是树状数组的应用
309 - 最佳买卖股票时机含冷冻期
经典的买卖股票问题了-> 状态机模型
这里含冷冻期的状态共有三个:
在冷冻期
已买入
今天卖出
有没有冷冻期的区别无非是把之前只有一个的卖出状态 拆成了今天卖出(手里有股票且不能买 和 在冷冻期(手里有股票且可以买 两个状态
同样的 只需要前一个状态 那个空间可以优化成o(1) 注意缓存历史状态就行了
310 - 最小高度树
记录最大距离最小的点

剩下的细节大概就是要建双向边,dfs的时候额外记录father就行了
Last updated