[Leetcode Weekly Contest]309
链接:LeetCode
[Leetcode]2399. 检查相同字母间的距离
给你一个下标从 0 开始的字符串 s ,该字符串仅由小写英文字母组成,s 中的每个字母都 恰好 出现 两次 。另给你一个下标从 0 开始、长度为 26 的的整数数组 distance 。
字母表中的每个字母按从 0 到 25 依次编号(即,'a' -> 0, 'b' -> 1, 'c' -> 2, ... , 'z' -> 25)。
在一个 匀整 字符串中,第 i 个字母的两次出现之间的字母数量是 distance[i] 。如果第 i 个字母没有在 s 中出现,那么 distance[i] 可以 忽略 。
如果 s 是一个 匀整 字符串,返回 true ;否则,返回 false 。
遍历即可。
class Solution {
public boolean checkDistances(String s, int[] distance) {
HashMap<Character, Integer> hash = new HashMap<>();
HashSet<Character> set = new HashSet<>();
for(int i=0;i<s.length();++i) {
Character ch = s.charAt(i);
set.add(ch);
hash.put(ch, i-hash.getOrDefault(ch, 0));
}
for(int i=0;i<26;++i) {
Character ch = (char)('a'+i);
if(!set.contains(ch)) continue;
if(hash.getOrDefault(ch, 0)-1 != distance[i]) return false;
}
return true;
}
}
[Leetcode]2400. 恰好移动 k 步到达某一位置的方法数目
给你两个 正 整数 startPos 和 endPos 。最初,你站在 无限 数轴上位置 startPos 处。在一步移动中,你可以向左或者向右移动一个位置。
给你一个正整数 k ,返回从 startPos 出发、恰好 移动 k 步并到达 endPos 的 不同 方法数目。由于答案可能会很大,返回对 109 + 7 取余 的结果。
如果所执行移动的顺序不完全相同,则认为两种方法不同。
注意:数轴包含负整数。
求组合数。
称 startPos 指向 endPos 的方向为正方向,startPos 远离 endPos 的方向为负方向。设从 startPos 出发,往正方向走了 a 步,往负方向走了(k−a) 步后到达 endPos,根据组合数的定义可知答案为 \(C_k^a\) (k 步里选 a 步走正方向)。
以用 \(C_i^j = C_{i - 1}^j + C_{i - 1}^{j - 1}\)的递推式 \(\mathcal{O}(k^2)\)求组合数。
注意求组合数不能直接用 \(C_i^j\) 算,一旦溢出或者取余再做除法结果就会出错
class Solution {
int MOD = (int)1E9+7;
HashMap<Pair<Integer, Integer>, Integer> hash = new HashMap<>();
public int numberOfWays(int startPos, int endPos, int k) {
int diff = Math.abs(startPos-endPos);
if(((k+diff) & 1) != 0 || k < diff) return 0;
int oneDirection = (k+diff)/2;
return computeC(k, oneDirection);
}
public int computeC(int n, int m) {
if(n == m) return 1;
if(m == 1) return n;
if(n<m) return 0;
Pair<Integer,Integer> key = new Pair<>(n,m);
if(hash.containsKey(key)) return hash.get(key);
int res = (computeC(n-1,m)% MOD + computeC(n-1,m-1)% MOD)% MOD;
hash.put(key, res);
return res;
}
}
[Leetcode]2401. 最长优雅子数组
给你一个由 正 整数组成的数组 nums 。
如果 nums 的子数组中位于 不同 位置的每对元素按位 与(AND)运算的结果等于 0 ,则称该子数组为 优雅 子数组。
返回 最长 的优雅子数组的长度。
子数组 是数组中的一个 连续 部分。
注意:长度为 1 的子数组始终视作优雅子数组。
子数组 是数组中的一个 连续 部分:联想到滑动窗口。
记 mask 表示 优雅 子数组中的元素之和,初始时 mask=0。
对于当前元素 num,
- 若 \(mask \ \&\ num = 0\),则可将 num 添加到 优雅 子数组中:\(mask = mask + num\) .
- 否则,需要将当前窗口(即当前的优雅子数组)中最左侧的元素 nums[left] 依次删除: \(mask = mask - nums[left]\) ,
直至满足要求。
遍历元素过程中,记录下最大窗口长度即可。
因为异或刚好可以把 & = 1(失败)的元素去掉,所以可以再用双指针简化
class Solution {
public int longestNiceSubarray(int[] nums) {
int left = 0, mask = 0, res = 0;
for(int right=0;right<nums.length;right++) {
while((mask & nums[right]) > 0) {
mask ^= nums[left];
left ++;
}
mask ^= nums[right];
res = Math.max(res, right-left+1);
}
return res;
}
}
[Leetcode] 6170. 会议室 III
给你一个整数 n ,共有编号从 0 到 n - 1 的 n 个会议室。
给你一个二维整数数组 meetings ,其中 meetings[i] = [starti, endi] 表示一场会议将会在 半闭 时间区间 [starti, endi) 举办。所有 starti 的值 互不相同 。
会议将会按以下方式分配给会议室:
- 每场会议都会在未占用且编号 最小 的会议室举办。
- 如果没有可用的会议室,会议将会延期,直到存在空闲的会议室。延期会议的持续时间和原会议持续时间 相同 。
- 当会议室处于未占用状态时,将会优先提供给原 开始 时间更早的会议。
返回举办最多次会议的房间 编号 。如果存在多个房间满足此条件,则返回编号 最小 的房间。
半闭区间 [a, b) 是 a 和 b 之间的区间,包括 a 但 不包括 b 。
双堆模拟.整个过程需要维护两件事:一是当前在进行的会议的结束时间和占用的会议室号,二是当前还有哪些会议室可用。更早结束的会议可更早将会议室腾出来,因此可以用优先队列维护会议的结束时间;而优先采用下标更小的会议室,也可以用一个优先队列(或者 TreeSet)保存所有可用的会议室下标。
维护过程:由于会议优先分配给开始时间早的,可以先按开始时间排序,顺序遍历会议。当前会议分配前,首先检查优先队列中有没有结束时间早于当前会议开始时间的会议,如果有,直接弹出这些会议即可。分配时会遇到两种情况:一是有空会议室,直接从另一个堆中拿出一个空会议室即可;二是没有空会议室,从优先队列中拿出一个最早结束的会议室分配给它,不过这种情况下当前会议的结束时间要设置为 上一个会议的结束时间 + 当前会议的持续时间,因为需要考虑等待的时间。
class Solution {
private class Pair<K, V> {
public K first;
public V second;
public Pair(K first, V second) {
this.first = first;
this.second = second;
}
}
public int mostBooked(int n, int[][] meetings) {
// 按开始时间排序
Arrays.sort(meetings, (x, y) -> x[0] - y[0]);
int[] count = new int[n];
// TreeSet 记录此时所有可用的会议室(也可以用 PriorityQueue)
TreeSet<Integer> meetingRoom = new TreeSet<>();
for (int i = 0; i < n; i++)
meetingRoom.add(i);
// 优先队列记录 {结束时间,占用的会议室},优先按照结束时间排序
PriorityQueue<Pair<Long, Integer>> heap = new PriorityQueue<>((x, y) -> Long.compare(x.first, y.first) == 0 ? x.second - y.second : Long.compare(x.first, y.first));
// 顺序遍历会议室,先遍历到的肯定是开始时间早的
for (int[] meeting : meetings) {
// 如果有结束时间早于当前会议开始时间的会议,都先弹出优先队列(不会对后面的会议造成影响)
while (!heap.isEmpty() && meeting[0] >= heap.peek().first)
meetingRoom.add(heap.poll().second);
// 如果 TreeSet 中获取不到空的会议室,就弹出最早结束的会议把会议室腾出来,但是结束时间要设置为"弹出会议的结束时间 + 当前会议的持续时间",因为要等待会议结束
if (meetingRoom.isEmpty()) {
Pair<Long, Integer> cur = heap.poll();
count[cur.second]++;
heap.offer(new Pair<>(cur.first + (long) meeting[1] - meeting[0], cur.second));
}
// 有空会议室直接分配即可
else {
int get = meetingRoom.first();
heap.offer(new Pair<>((long) meeting[1], get));
count[get]++;
meetingRoom.remove(get);
}
}
// 最后计算最大的会议室下标
int max = 0;
for (int i = 1; i < n; i++)
if (count[max] < count[i])
max = i;
return max;
}
}
参考:
LeetCode