10-周赛336总结
过了三题,第二题因为越界WA了几次,最后回头看终于发现了。第四题想到了合并区间,但是写不出来,还是挺满足了。
10次周赛,难题还是做不出来,中等题越来越熟练了,感觉目前周赛对我的提升没有那么大,也许需要集中时间刷难题了
统计范围内的元音字符串数【2586】
给你一个下标从 0 开始的字符串数组 words
和两个整数:left
和 right
。
如果字符串以元音字母开头并以元音字母结尾,那么该字符串就是一个 元音字符串 ,其中元音字母是 'a'
、'e'
、'i'
、'o'
、'u'
。
返回 words[i]
是元音字符串的数目,其中 i
在闭区间 [left, right]
内。
- 思路
判断在闭区间[left, right]
内的单词是否是元音字符串,记录是元音字符串的个数 - 实现
class Solution { public int vowelStrings(String[] words, int left, int right) { int res = 0; while (left <= right){ String word = words[left]; if (isVowel(word, 0) && isVowel(word, word.length() - 1)){ res++; } left++; } return res; } public boolean isVowel(String word, int index){ char c = word.charAt(index); if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'){ return true; } return false; } }
复杂度
- 时间复杂度:O(n)
- 空间复杂度:O(1)
重排数组以得到最大前缀分数【LC2687】
给你一个下标从 0 开始的整数数组 nums
。你可以将 nums
中的元素按 任意顺序 重排(包括给定顺序)。
令 prefix
为一个数组,它包含了 nums
重新排列后的前缀和。换句话说,prefix[i]
是 nums
重新排列后下标从 0
到 i
的元素之和。nums
的 分数 是 prefix
数组中正整数的个数。
返回可以得到的最大分数。
- 思路:贪心
排序后的数组,要尽可能使前缀和数组prefix[i]越大,获得的分数越高,因此应将数组从大到小排 序,求前缀和,统计prefix[i]是正整数的个数,如果prefix[i]小于0,那么直接返回个数即可,接下来的 前缀和均小于0
- 实现
观察数据范围,在累加过程中,可能会出现越界,因此使用long类型存储前缀和
class Solution { public int maxScore(int[] nums) { Arrays.sort(nums); int n = nums.length; long sum = 0L; int count = 0; for (int i = nums.length - 1; i >= 0; i--){ sum += nums[i]; if (sum > 0){ count++; }else { return count; } } return count; } }
复杂度
- 时间复杂度:O(n)
- 空间复杂度:O(1)
统计美丽子数组数目【LC2588】
给你一个下标从 0 开始的整数数组nums
。每次操作中,你可以:
- 选择两个满足
0 <= i, j < nums.length
的不同下标i
和j
。 - 选择一个非负整数
k
,满足nums[i]
和nums[j]
在二进制下的第k
位(下标编号从 0 开始)是1
。 - 将
nums[i]
和nums[j]
都减去2k
。
如果一个子数组内执行上述操作若干次后,该子数组可以变成一个全为 0
的数组,那么我们称它是一个 美丽 的子数组。
请你返回数组 nums
中 美丽子数组 的数目。
子数组是一个数组中一段连续 非空 的元素序列。
思路:位运算+哈希表+前缀和
- 由于每次可以执行的操作是选择数组中某一位均为1的两个数,将其减去2k,于是可以想到位运算的异或操作,相同的数异或结果为0
- 如果子数组异或结果为0,那么该子数组为美丽子数组,因此可以将题意转换为,求出子数组异或结果为0的个数
- 借鉴前缀和数组的概念,我们可以记录nums[i]对应的异或数组,假设
nums[0,i]
对应的异或值为a ,nums[0,j]
对应的异或值也为a,那么nums[i+1,j]对应的异或值为0【任何一个数与0异或的结果还为0】
因此,我们枚举子数组的每一个右端点
i,将nums[0,i-1]
的异或结果存放在哈希表中,哈希表的键值为异或结果,key值为出现的次数,那么以nums[i]
为右端点的美丽子数组的数目即为其对应的异或结果在这之前出现的次数
实现
class Solution { public long beautifulSubarrays(int[] nums) { long res = 0L; Map<Integer,Long> map = new HashMap<>(); map.put(0, 1L); int cur = 0; for (int i = 0; i < nums.length; i++){ cur ^= nums[i]; res += map.getOrDefault(cur, 0L); map.put(cur, map.getOrDefault(cur, 0L) + 1); } return res; } }
完成所有任务的最少时间【LC2589】
你有一台电脑,它可以 同时 运行无数个任务。给你一个二维整数数组 tasks
,其中 tasks[i] = [starti, endi, durationi]
表示第 i
个任务需要在 闭区间 时间段 [starti, endi]
内运行 durationi
个整数时间点(但不需要连续)。
当电脑需要运行任务时,你可以打开电脑,如果空闲时,你可以将电脑关闭。
请你返回完成所有任务的情况下,电脑最少需要运行多少秒。
贪心+暴力
- 思路
- 首先将任务数组
tasks
按右端点升序排序,那么右侧的任务要么与左侧的任务没有交集,要么包含左侧区间后缀 - 因此我们选择工作时间点时,新增时间点应尽可能选该区间后缀时间点,这样与下个区间的重叠时间点更多,最终完成所有任务时电脑需要运行的秒数最少
- 实现
首先将任务数组tasks
按右端点升序排序,然后统计每个区间内已有的电脑运行时间点,如果个数小于,那么从本区间的末尾开始新增时间点
class Solution { public int findMinimumTime(int[][] tasks) { Arrays.sort(tasks, (o1, o2) -> o1[1] - o2[1]); int maxTime = tasks[tasks.length - 1][1]; int ans = 0; boolean[] flag = new boolean[maxTime + 1]; for (int[] task : tasks){ // 统计个数 int count = 0; for (int i = task[0]; i <= task[1]; i++){ if (flag[i]){ count++; } } if (count < task[2]){ for (int i = task[1]; i >= task[0] && count < task[2]; i--){ if(!flag[i]){ flag[i] = true; count++; ans++; } } } } return ans; } }