第 336 场周赛自我总结与反思


今天是菜鸡第一次参加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

路漫漫其修远兮,汲取教训,不断学习提升自己,争取下次做得更好!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值