记录 LeetCode 第 293 场周赛
本次周赛不负众望的菜,写出三题(应该能上分),第四题在最后十几分钟想到一点思路了,但是对于合并区间还没有很明确的想法,码了一会感觉有点麻烦就没继续写了没吃早饭,当时肚子好饿。
战局详情:
排名 | 用户名 | 得分 | 完成时间 | 题目1(3) | 题目2(4) | 题目3(5) | 题目4(7) |
---|---|---|---|---|---|---|---|
1930/7356 | Juruoer | 12 | 0:54:04 | 0:14:17 | 0:20:47 | 0:54:04 |
题目及解答
移除字母异位词后的结果数组
给你一个下标从 0 开始的字符串 words
,其中 words[i]
由小写英文字符组成。
在一步操作中,需要选出任一下标 i
,从 words
中 删除 words[i]
。其中下标 i
需要同时满足下述两个条件:
0 < i < words.length
words[i - 1]
和words[i]
是 字母异位词 。
只要可以选出满足条件的下标,就一直执行这个操作。
在执行所有操作后,返回 words
。可以证明,按任意顺序为每步操作选择下标都会得到相同的结果。
字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。例如,"dacb"
是 "abdc"
的一个字母异位词。
提示:
1 <= words.length <= 100
1 <= words[i].length <= 10
words[i]
由小写英文字母组成
示例一:
输入:words = [“abba”,“baba”,“bbaa”,“cd”,“cd”]
输出:[“abba”,“cd”]
解释:
获取结果数组的方法之一是执行下述步骤:
- 由于 words[2] = “bbaa” 和 words[1] = “baba” 是字母异位词,选择下标 2 并删除 words[2] 。
现在 words = [“abba”,“baba”,“cd”,“cd”] 。 - 由于 words[1] = “baba” 和 words[0] = “abba” 是字母异位词,选择下标 1 并删除 words[1] 。
现在 words = [“abba”,“cd”,“cd”] 。 - 由于 words[2] = “cd” 和 words[1] = “cd” 是字母异位词,选择下标 2 并删除 words[2] 。
现在 words = [“abba”,“cd”] 。
无法再执行任何操作,所以 [“abba”,“cd”] 是最终答案。
示例二:
输入:words = [“a”,“b”,“c”,“d”,“e”]
输出:[“a”,“b”,“c”,“d”,“e”]
解释:
words 中不存在互为字母异位词的两个相邻字符串,所以无需执行任何操作。
解决方案:
既然已经证明:按任意顺序为每步操作选择下标都会得到相同的结果,那么只需要自左向右遍历一遍数组,对于一个新的元素,比较其和结果集中的当前最后一个元素是否是字母异位词,若是,则跳过该元素,否则加入结果集。
如何判断两字符串是否为字母异位词?
- 方法一:统计字符串的每个字符个数,若两字符串每个字符的个数都相等,则为字母异位词,即我下面最终代码使用的方法
- 方法二:将每个字符串的字符按字母序排序,形成一个新的字符串数组,新的字符串数组中相同的两个字符串,其原字符串即为字母异位词:
1
2
3
4vector<string> _words = words;
for(int i = 0; i < _words.size(); ++i)
sort(_words[i].begin(), _words[i].end());
// 此时若 _words[i] == _words[j], 则 words[i] 和 words[j] 互为字母异位词
最终代码为:
1 | class Solution |
不含特殊楼层的最大连续楼层数
Alice 管理着一家公司,并租用大楼的部分楼层作为办公空间。Alice 决定将一些楼层作为 特殊楼层 ,仅用于放松。
给你两个整数 bottom
和 top
,表示 Alice 租用了从 bottom
到 top
(含 bottom
和 top
在内)的所有楼层。另给你一个整数数组 special
,其中 special[i]
表示 Alice 指定用于放松的特殊楼层。
返回不含特殊楼层的 最大 连续楼层数。
提示:
1 <= special.length <= 1e5
1 <= bottom <= special[i] <= top <= 1e9
special[i]
中的所有值 互不相同
示例一:
输入:bottom = 2, top = 9, special = [4,6]
输出:3
解释:下面列出的是不含特殊楼层的连续楼层范围:
- (2, 3) ,楼层数为 2 。
- (5, 5) ,楼层数为 1 。
- (7, 9) ,楼层数为 3 。
因此,返回最大连续楼层数 3 。
示例二:
输入:bottom = 6, top = 8, special = [7,6,8]
输出:0
解释:每层楼都被规划为特殊楼层,所以返回 0 。
解决方案:
先对 special
排序,然后求出 special
中相邻两个元素的差值最大值即可。
对 bottom
和 top
进行特殊处理,以计算出 第一个连续不含特殊楼层和最后一个连续不含特殊楼层楼的层数:
1 | class Solution |
或者假设存在 bottom - 1
层和 top + 1
层,并且都是特殊楼层,则无需对 bottom
和 top
特殊处理:
1 | class Solution |
按位与结果大于零的最长组合
对数组 nums
执行 按位与 相当于对数组 nums
中的所有整数执行 按位与 。
- 例如,对
nums = [1, 5, 3]
来说,按位与等于1 & 5 & 3 = 1
。 - 同样,对
nums = [7]
而言,按位与等于7
。
给你一个正整数数组 candidates
。计算 candidates
中的数字每种组合下 按位与 的结果。 candidates
中的每个数字在每种组合中只能使用 一次 。
返回按位与结果大于 0
的 最长 组合的长度。
提示:
1 <= candidates.length <= 1e5
1 <= candidates[i] <= 1e7
示例一:
输入:candidates = [16,17,71,62,12,24,14]
输出:4
解释:组合 [16,17,62,24] 的按位与结果是 16 & 17 & 62 & 24 = 16 > 0 。
组合长度是 4 。
可以证明不存在按位与结果大于 0 且长度大于 4 的组合。
注意,符合长度最大的组合可能不止一种。
例如,组合 [62,12,24,14] 的按位与结果是 62 & 12 & 24 & 14 = 8 > 0 。
示例二:
输入:candidates = [8,8]
输出:2
解释:最长组合是 [8,8] ,按位与结果 8 & 8 = 8 > 0 。
组合长度是 2 ,所以返回 2 。
解决方案:
由按位与的特性,可以知道,若 数集{x} 所有数按位与的结果大于 0
,则至少存在一个下标 i
,该 数集{x} 的所有数字的二进制在此下标处均为 1
。则只需要找出数组 candidates
中所有数的二进制为 1
的位,并找出含 1
的数字最多的位(表达能力不太好,写的有点绕,看下面表格即可明白)。
例如对示例一所有数有:
十进制 | 二进制(此处仅展示后8位) |
---|---|
16 | 0001 0000 |
17 | 0001 0001 |
71 | 0100 0111 |
62 | 0011 1110 |
12 | 0000 1100 |
24 | 0001 1000 |
14 | 0000 1110 |
总计各位为1的数字个数 | 0114 4432 |
可知 1
的个数最多的位(自右向左,自0开始)有第2位、第3位、第4位,均含有4个此位为1的数字,分别为:
- 第2为为1的数:71、62、12、14
- 第3为为1的数:62、12、24、14
- 第4为为1的数:16、17、62、24
故只需要定义一个数组 cnts
,cnts[i]
表示 candidates
中二进制第 i
位为 1
的数字个数,则 cnts
的最大值即为所求答案。
定义一个整数数组 t
,且 t[i]
的二进制仅在第 i
位为 1
,其他位为 0
,随后遍历数组 candidates
,若 candidates[i] & t[j] == 1
,则说明 candidates[i]
的二进制在第 j
位为 1
,需要 ++cnts[j];
。
可以写出代码:
1 | class Solution |
统计区间中的整数数目
给你区间的 空 集,请你设计并实现满足要求的数据结构:
- 新增:添加一个区间到这个区间集合中。
- 统计:计算出现在 至少一个 区间中的整数个数。
实现 CountIntervals
类:
CountIntervals()
使用区间的空集初始化对象void add(int left, int right)
添加区间[left, right]
到区间集合之中。int count()
返回出现在 至少一个 区间中的整数个数。
注意:区间[left, right]
表示满足left <= x <= right
的所有整数x
。
提示:
1 <= left <= right <= 1e9
- 最多调用
add
和count
方法 总计1e5
次 - 调用
count
方法至少一次
示例一:
输入
[“CountIntervals”, “add”, “add”, “count”, “add”, “count”]
[[], [2, 3], [7, 10], [], [5, 8], []]
输出
[null, null, null, 6, null, 8]
解释:
1 | CountIntervals countIntervals = new CountIntervals(); // 用一个区间空集初始化对象 |
解决方案:
使用 pair<int, int>
来表示一个区间,first
为区间的右边界,second
为左边界。
使用一个集合 set
存储所有合并后的区间集。
使用 cnt
表示区间集 s
包含的所有整数个数。
对于一个新的区间 [left, right]
,作如下操作:
- 在
s
中找到所有右边界大于等于left - 1
, 左边界小于等于right + 1
的区间集to_sease
。 - 设区间集合
to_sease
的第一个区间为a = [a1, a2]
,最后一个区间为b = [b1, b2]
。 - 将
to_sease
中所有区间从s
中删除,且cnt
减去这些区间包含的整数个数。 - 插入
to_sease
和[left, right]
合并后的区间[L, R]
,其中L = min(left, a1); R = max(right, b2)
,形成新的区间集s
,且cnt
加上这个区间包含的整数个数。
1 | class CountIntervals |