Leetcode 每日一题和每日一题的下一题刷题笔记 1/30
写在前面
这是我参与更文挑战的第1天,活动详情查看:更文挑战
快要毕业了,才发现自己被面试里的算法题吊起来锤。没办法只能以零基础的身份和同窗们共同加入了力扣刷题大军。我的同学们都非常厉害,他们平时只是谦虚,口头上说着自己不会,而我是真的不会。。。乘掘金鼓励新人每天写博客,我也凑个热闹,记录一下每天刷的前两道题,这两道题我精做。我打算每天刷五道题,其他的题目嘛,也只能强行背套路了,就不发在博客里了。
本人真的只是一个菜鸡,解题思路什么的就不要从我这里参考了,编码习惯也需要改进,各位如果想找刷题高手请教问题我觉得去找 宫水三叶的刷题日记 这位大佬比较好。我在把题目做出来之前尽量不去看题解,以免和大佬的内容撞车。
另外我也希望有得闲的大佬提供一些更高明的解题思路给我,欢迎讨论哈!
好了废话不多说开始第一天的前两道题吧!
2021.6.1 每日一题
规则里是从第0天开始吃的,嗯,这很程序员。吃到第n天一共吃了n + 1天的糖。一天至少吃1颗,至多吃dailyCap颗,所以从最开始到现在吃掉的糖总数是可以用一个范围来表示的,即[n + 1, (n + 1) * dailyCap]。这里尽量不要用i这个下标,以免和第i个请求混淆。现在用前缀和的意图已经很明显了,在 favoriteDay 这天,这个最大范围 (favoriteDay + 1) * dailyCap 超过 favoriteType 前面类别糖果的总数,就能在最喜欢的这一天吃到最想吃的糖果。要超过(不能等于)的这个总数就是 sum[favoriteType - 1],前缀和用到这了。一开始我觉得这样这道题就没毛病了,结果想的太简单,万一一天一颗糖吃到最喜欢的那一天前把最喜欢的糖的最后一颗给恰了,那最喜欢的一天不就哭哭了吗。。。果然这需要另一个条件来约束,最小范围 favoriteDay + 1 不能超过(不能等于,等于就哭哭了) sum[favoriteType] 这个值。
接下来撸码,我的 C++ 代码是这个(第一个用例很贴心,估计 int 不够用了已经,直接 long long 走起):
class Solution {
public:
vector<bool> canEat(vector<int>& candiesCount, vector<vector<int>>& queries) {
int n = candiesCount.size();
vector<long long> sum(n);
sum[0] = candiesCount[0];
for (int i = 1; i < n; ++i) {
sum[i] = sum[i - 1] + candiesCount[i];
}
vector<bool> ans;
for (const auto& q: queries) {
int favoriteType = q[0], favoriteDay = q[1], dailyCap = q[2];
long long x1 = favoriteDay + 1;
long long x2 = (long long)(favoriteDay + 1) * dailyCap;
long long y1;
if (favoriteType == 0) {
y1 = 1;
} else {
y1 = sum[favoriteType - 1] + 1;
}
long long y2 = sum[favoriteType];
if (!((x1 > y2) || (x2 < y1))) {
ans.push_back(true);
} else {
ans.push_back(false);
}
}
return ans;
}
};
老笨蛋了。。。终于过了。
对了,建议各位尝试一下 C++ 11 之后的 auto,有种在耍 Python 的感觉(干啥啥不行偷懒第一名)。
非常不建议参考我的代码,规范优雅快(三者占其二)的代码请参考官方解答。
进入第二道题吧。。。
2021.6.1 每日一题下面的题
点个随机,看看系统给我分配了什么好题:
括号匹配问题,一个左对两个右,我记得以前似乎看到过一左一右用栈来解决比较方便。来个直截了当的笨办法,应该没有用到栈,从左往右遍历。当前是左,看到的左+=1,看下一个。当前是右,看到的左-=1,看下一个是什么,是左,插入次数+=1(再插一个右),看下一个(看的就是左,请时刻注意当前在哪里);是右,直接看下两个(下一个是右,但是和前面成对儿了,所以不看下一个,直接看下两个,请时刻注意当前在哪里)。这样直到遍历完整个字符串,能得到一个 看到的左 和一个 插入次数。插入次数 一定是正的,这个先晾一边,看到的左 可正可负还可以是 0,如果是 0 最好,现在都匹配上了,直接输出 插入次数。如果是正,说明还缺右,看到的左 的绝对值乘以 2 加上 插入次数 是最终的结果。如果是负,说明缺左,看到的左 的绝对值加上 插入次数 就是最终的结果。
笨办法 C++ 代码如下:
class Solution {
public:
int minInsertions(string s) {
int insertions = 0;
int leftCount = 0;
int length = s.size();
int index = 0;
while (index < length) {
char c = s[index];
if (c == '(') {
leftCount++;
index++;
} else {
leftCount--;
if (index < length - 1 && s[index + 1] == '(') {
insertions++;
index++;
}
if (index < length - 1 && s[index + 1] == ')') {
index += 2;
}
}
}
if (leftCount > 0) {
insertions += leftCount * 2;
return insertions;
} else if (leftCount < 0) {
insertions -= leftCount;
return insertions;
}
return insertions;
}
};
非常的 nice,它超时了。。。
瞎改改,让 看到的左 永远是正数,底下循环外面就没有判断了,循环里面先判断 看到的左 是不是正数,是正数 看到的左-=1,不是正数,要补一个左,插入次数+=1。然后判断跟着右的是不是还是右,是,看下两个(注意当前在哪里),不是,要补一个右,插入次数+=1,看下一个(注意当前在哪里)。
然后神奇的发现我的代码和官方题解似乎没啥差别。。。那就不粘贴了。
然后来用栈解决一下这个问题吧,我应该不会翻车吧。。。C++ 代码如下
class Solution {
public:
int minInsertions(string s) {
int length_s = s.length();
stack<char> stack_s;
int insertions = 0;
for (int i = 0; i < length_s; i++){
if (s[i] == '(') {
stack_s.push('(');
} else {
if (!(i < length_s - 1 && s[i + 1] == ')')) {
insertions++;
} else {
i++;
}
if (stack_s.empty()) {
insertions++;
} else {
stack_s.pop();
}
}
}
return insertions + stack_s.size() * 2;
}
};
我个人感觉用了不如不用,C++ 这么写还没之前性能好。然后我发现一个小技巧,当前不是左的情况下里面两对 if-else 这里其实是能交换顺序的,尝试换一下也许性能有提升(我的代码里换了一下性能下降了。。。)
小结
今天学到了什么:前缀和,C++11 的 auto 关键字,括号匹配