第 336 场周赛自我总结与反思
- 1、[第一题:统计范围内的元音字符串数](https://leetcode.cn/problems/count-the-number-of-vowel-strings-in-range/)
- 2、[第二题:重排数组以得到最大前缀分数](https://leetcode.cn/problems/rearrange-array-to-maximize-prefix-score/)
- 3、[第三题:统计美丽子数组数目](https://leetcode.cn/problems/count-the-number-of-beautiful-subarrays/)
- 4、[第四题:完成所有任务的最少时间](https://leetcode.cn/problems/minimum-time-to-complete-all-tasks/)
今天是菜鸡第一次参加leetcode的周赛,结果也是不出意外地比较打击自己的心情,特此总结与反思本次周赛暴露出来的问题,记录一下自己的改进与成长。
1、第一题:统计范围内的元音字符串数
题目:
给你一个下标从 0 开始的字符串数组 words 和两个整数:left 和 right 。
如果字符串以元音字母开头并以元音字母结尾,那么该字符串就是一个 元音字符串 ,其中元音字母是 ‘a’、‘e’、‘i’、‘o’、‘u’ 。
返回 words[i] 是元音字符串的数目,其中 i 在闭区间 [left, right] 内。
示例:
输入:words = [“are”,“amy”,“u”], left = 0, right = 2
输出:2
解释:
-“are” 是一个元音字符串,因为它以 ‘a’ 开头并以 ‘e’ 结尾。
-“amy” 不是元音字符串,因为它没有以元音字母结尾。
-“u” 是一个元音字符串,因为它以 ‘u’ 开头并以 ‘u’ 结尾。
-在上述范围中的元音字符串数目为 2 。
提示:
1 <= words.length <= 1000
1 <= words[i].length <= 10
words[i] 仅由小写英文字母组成
0 <= left <= right < words.length
我的代码:
纯枚举出来的
class Solution {
public:
int vowelStrings(vector<string>& words, int left, int right) {
int res=0;
for (int i = left; i <= right; i++) {
int s_len = words[i].size()-1;
if ((words[i][0] == 'a' || words[i][0] == 'e' || words[i][0] == 'i' || words[i][0] == 'o' || words[i][0] == 'u')
&& (words[i][s_len] == 'a' || words[i][s_len] == 'e' || words[i][s_len] == 'i' || words[i][s_len] == 'o' || words[i][s_len] == 'u')) {
res++;
}
}
return res;
}
};
大佬代码:
class Solution {
public:
int vowelStrings(vector<string>& words, int left, int right) {
auto gao = [&](char c) {
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
};
int ans = 0;
for (int i = left; i <= right; i++) if (gao(words[i][0]) && gao(words[i][words[i].size() - 1])) ans++;
return ans;
}
};
作者:TsReaper
链接:https://leetcode.cn/circle/discuss/WuUSIw/view/jSsyEA/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
总结与反思
- 在看到大佬题解中的 auto gao = [&](char c) 语句的时候我还愣了一下,搜索以后才猛然想起来是匿名函数。
- 看来自己的C++的基础还需要补强啊。
auto toNumber = [&](string const& s) -> unsigned {
…
}
[]:定义匿名函数
[&]:以引用形式捕获所有外部变量,也就是外部变量均可用
(string const &s) :匿名函数的参数
->:定义匿名函数
unsigned:函数返回值类型
{…}:函数实现体
————————————————
版权声明:本文为CSDN博主「KevinYzy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/huluwaaaa/article/details/103225311
2、第二题:重排数组以得到最大前缀分数
题目:
给你一个下标从 0 开始的整数数组 nums 。你可以将 nums 中的元素按 任意顺序 重排(包括给定顺序)。
令 prefix 为一个数组,它包含了 nums 重新排列后的前缀和。换句话说,prefix[i] 是 nums 重新排列后下标从 0 到 i 的元素之和。nums 的 分数 是 prefix 数组中正整数的个数。返回可以得到的最大分数。
示例
输入:nums = [2,-1,0,1,-3,3,-3]
输出:6
解释:数组重排为 nums = [2,3,1,-1,-3,0,-3] 。
prefix = [2,5,6,5,2,2,-1] ,分数为 6 。
可以证明 6 是能够得到的最大分数。
提示:
1 <= nums.length <= 105
-106 <= nums[i] <= 106
我的代码:
本题中我的思路是,要想获取最大的分数,就将数组从大到小排列,这样就能使得数组中较大的数始终处在前面,前缀和一开始就处在最大。
class Solution {
public:
int maxScore(vector<int>& nums) {
sort(nums.begin(), nums.end(), greater<long long>());
int res=0;
vector<long long> prefix;
long long sum = 0;
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
prefix.emplace_back(sum);
}
for (long long i : prefix) {
if (i > 0) {
res++;
}
}
return res;
}
};
大佬代码:
class Solution {
public:
int maxScore(vector<int>& nums) {
int n = nums.size();
// 从大到小排序
sort(nums.begin(), nums.end(), [](int a, int b) {
return a > b;
});
// 计算前缀和,统计答案
int ans = 0;
long long sm = 0;
for (int i = 0; i < n; i++) {
sm += nums[i];
if (sm > 0) ans++;
}
return ans;
}
};
作者:TsReaper
链接:https://leetcode.cn/circle/discuss/WuUSIw/view/jSsyEA/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
总结与反思
- 要注意数值大小的范围,前两次错误提交就是没有注意范围错用int接收sum值,导致溢出
- 本题中我与带佬的思路一致,但多此一举新建了个prefix数组来存放sum,然后再遍历数组找正数。在今后的代码实现中,可以多注意此类问题,简化代码。
3、第三题:统计美丽子数组数目
题目:
给你一个下标从 0 开始的整数数组nums 。每次操作中,你可以:
选择两个满足 0 <= i, j < nums.length 的不同下标 i 和 j 。
选择一个非负整数 k ,满足 nums[i] 和 nums[j] 在二进制下的第 k 位(下标编号从 0 开始)是 1 。
将 nums[i] 和 nums[j] 都减去 2k 。
如果一个子数组内执行上述操作若干次后,该子数组可以变成一个全为 0 的数组,那么我们称它是一个 美丽 的子数组。
请你返回数组 nums 中 美丽子数组 的数目。
子数组是一个数组中一段连续 非空 的元素序列。
示例
输入:nums = [4,3,1,2,4]
输出:2
解释:nums 中有 2 个美丽子数组:[4,3,1,2,4] 和 [4,3,1,2,4] 。
- 按照下述步骤,我们可以将子数组 [3,1,2] 中所有元素变成 0 :
- 选择 [3, 1, 2] 和 k = 1 。将 2 个数字都减去 21 ,子数组变成 [1, 1, 0] 。
- 选择 [1, 1, 0] 和 k = 0 。将 2 个数字都减去 20 ,子数组变成 [0, 0, 0] 。
- 按照下述步骤,我们可以将子数组 [4,3,1,2,4] 中所有元素变成 0 :
- 选择 [4, 3, 1, 2, 4] 和 k = 2 。将 2 个数字都减去 22 ,子数组变成 [0, 3, 1, 2, 0] 。
- 选择 [0, 3, 1, 2, 0] 和 k = 0 。将 2 个数字都减去 20 ,子数组变成 [0, 2, 0, 2, 0] 。
- 选择 [0, 2, 0, 2, 0] 和 k = 1 。将 2 个数字都减去 21 ,子数组变成 [0, 0, 0, 0, 0] 。
提示:
1 <= nums.length <= 105
0 <= nums[i] <= 106
我的错误代码
class Solution {
public:
//获取k值
int findK(int a, int b) {
int k = 0;
bitset<256> b1(a);
bitset<256> b2(b);
for (int i = 0; i < 256; i++) {
if (b1[i] == 1 && b2[i] == 1) {
break;
}
k++;
}
return k;
}
//数组判全0
bool isZero(vector<int>& n) {
for (int i : n) {
if (i != 0) {
return false;
}
}
return true;
}
//递归计算
void bs(vector<int>& nums, long long& res, int left, int right) {
if (isZero(nums)) {
res++;
return;
}
int k = findK(nums[left], nums[right]);
int sub = pow(2, k);
nums[left] -= sub;
nums[right] -= sub;
if (nums[left] == 0)left++;
for (int i = left + 1; i < nums.size(); i++) {
if (nums[i] != 0) {
right = i;
break;
}
}
bs(nums, res, left, right);
}
long long beautifulSubarrays(vector<int>& nums) {
long long res = 0;
for (int i = 0; i < nums.size(); i++) {
for (int j = 0; j < i; j++) {
vector<int>::const_iterator first1 = nums.begin() + i;
vector<int>::const_iterator last1 = nums.end() - j;
vector<int> v(first1, last1);
bs(v, res, i, v.size()-1);
}
}
return res;
}
};
大佬代码:
class Solution {
public:
long long beautifulSubarrays(vector<int>& nums) {
int n = nums.size();
unordered_map<int, int> cnt;
cnt[0] = 1;
int now = 0;
long long ans = 0;
for (int i = 0; i < n; i++) {
now ^= nums[i];
ans += cnt[now];
cnt[now]++;
}
return ans;
}
};
作者:TsReaper
链接:https://leetcode.cn/circle/discuss/WuUSIw/view/jSsyEA/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
总结与反思
- 我的思路是,截取nums的子数组,然后通过递归不断重复减法操作和修正左右指针,如果最后得到一个全为0的数组,即该子串为美丽子数组。但是我对于递归的实现似乎还不太明确,这也导致我花了一个小时都还是失败,但是我感觉我的思路似乎是可以解出来?
- 大佬的思路是,由于每次操作要减去两个数的 2 k 2^k 2k,那么所有数中, 2 k 2^k 2k出现的次数之和必须是偶数。也就是所有数的异或和必须是0。
- 本题暴露出我很多问题:
- 刷题量还是太少了,思路跟不上,看到相应的题目不能很快反应过来应该要用什么算法和工具来解题;
- 对于位运算的相关算法基础薄弱,解题时应用位运算思路不敏感;
- STL中的bitset相关函数的使用方法很模糊,还需要多加使用熟悉;
- 还有递归操作的实现,还是要多学习实践一下,尤其是递归的思路要理顺,感觉这次确实有点混乱了。
4、第四题:完成所有任务的最少时间
题目:
你有一台电脑,它可以 同时 运行无数个任务。给你一个二维整数数组 tasks ,其中 tasks[i] = [starti, endi, durationi] 表示第 i 个任务需要在 闭区间 时间段 [starti, endi] 内运行 durationi 个整数时间点(但不需要连续)。
当电脑需要运行任务时,你可以打开电脑,如果空闲时,你可以将电脑关闭。
请你返回完成所有任务的情况下,电脑最少需要运行多少秒。
示例
输入:tasks = [[2,3,1],[4,5,1],[1,5,2]]
输出:2
解释:
-第一个任务在闭区间 [2, 2] 运行。
-第二个任务在闭区间 [5, 5] 运行。
-第三个任务在闭区间 [2, 2] 和 [5, 5] 运行。
电脑总共运行 2 个整数时间点。
提示:
1 <= tasks.length <= 2000
tasks[i].length == 3
1 <= starti, endi <= 2000
1 <= durationi <= endi - starti + 1
大佬代码:
class Solution {
public:
int findMinimumTime(vector<vector<int>>& tasks) {
int mx = 0;
for (auto &vec : tasks) mx = max(mx, vec[1]);
vector<int> e[mx + 2], v[mx + 2];
// 第一个不等式
for (int i = 1; i <= mx; i++) {
e[i].push_back(i - 1);
v[i].push_back(0);
}
// 第二个不等式
for (int i = 0; i < mx; i++) {
e[i].push_back(i + 1);
v[i].push_back(1);
}
// 第三个不等式
for (auto &vec : tasks) {
e[vec[1]].push_back(vec[0] - 1);
v[vec[1]].push_back(-vec[2]);
}
// 差分约束要建立超级源点
int S = mx + 1;
for (int i = 0; i <= mx; i++) {
e[S].push_back(i);
v[S].push_back(0);
}
// SPFA
const int INF = 1e9;
queue<int> q;
int dis[mx + 2];
for (int i = 0; i <= mx + 1; i++) dis[i] = INF;
bool vis[mx + 2];
memset(vis, 0, sizeof(vis));
q.push(S); dis[S] = 0; vis[S] = true;
while (!q.empty()) {
int sn = q.front(); q.pop();
vis[sn] = false;
for (int i = 0; i < e[sn].size(); i++) {
int fn = e[sn][i], val = v[sn][i];
if (dis[fn] <= dis[sn] + val) continue;
dis[fn] = dis[sn] + val;
if (vis[fn]) continue;
q.push(fn); vis[fn] = true;
}
}
return dis[mx] - dis[0];
}
};
作者:TsReaper
链接:https://leetcode.cn/circle/discuss/WuUSIw/view/jSsyEA/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
总结与反思
- 大佬使用的解法是差分约束,Ol Wiki差分约束介绍
- 自己是第一次见这个算法,算是长了见识了QAQ
路漫漫其修远兮,汲取教训,不断学习提升自己,争取下次做得更好!