[Leetcode Weekly Contest]285
链接:LeetCode
[Leetcode]2210. 统计数组中峰和谷的数量
给你一个下标从 0 开始的整数数组 nums 。如果两侧距 i 最近的不相等邻居的值均小于 nums[i] ,则下标 i 是 nums 中,某个峰的一部分。类似地,如果两侧距 i 最近的不相等邻居的值均大于 nums[i] ,则下标 i 是 nums 中某个谷的一部分。对于相邻下标 i 和 j ,如果 nums[i] == nums[j] , 则认为这两下标属于 同一个 峰或谷。
注意,要使某个下标所做峰或谷的一部分,那么它左右两侧必须 都 存在不相等邻居。
返回 nums 中峰和谷的数量。
遍历即可。
class Solution {
public int countHillValley(int[] nums) {
int res = 0;
Boolean increase = null;
int pre = nums[0];
for(var num:nums) {
if(num>pre) {
increase = true;
break;
}
else if(num<pre) {
increase = false;
break;
}
}
if(increase == null) return res;
for(var num:nums) {
if(num < pre) {
if(increase) res += 1;
increase = false;
}
else if(num>pre) {
if(!increase) res += 1;
increase = true;
}
pre = num;
}
return res;
}
}
[Leetcode]2211. 统计道路上的碰撞次数
在一条无限长的公路上有 n 辆汽车正在行驶。汽车按从左到右的顺序按从 0 到 n - 1 编号,每辆车都在一个 独特的 位置。
给你一个下标从 0 开始的字符串 directions ,长度为 n 。directions[i] 可以是 'L'、'R' 或 'S' 分别表示第 i 辆车是向 左 、向 右 或者 停留 在当前位置。每辆车移动时 速度相同 。
碰撞次数可以按下述方式计算:
当两辆移动方向 相反 的车相撞时,碰撞次数加 2 。
当一辆移动的车和一辆静止的车相撞时,碰撞次数加 1 。
碰撞发生后,涉及的车辆将无法继续移动并停留在碰撞位置。除此之外,汽车不能改变它们的状态或移动方向。
返回在这条道路上发生的 碰撞总次数 。
逻辑题,去掉往左右两边开的车之后,剩下的车必然会相撞。也可以通过栈模拟,来实现。
class Solution {
public int countCollisions(String directions) {
int start = 0, end = directions.length()-1;
while(start<=end) {
if(directions.charAt(start) != 'L') break;
start ++;
}
while(start<=end) {
if(directions.charAt(end) != 'R') break;
end --;
}
int count = 0;
for(var ch:directions.toCharArray()) {
if(ch == 'S') count ++;
}
return end-start+1 - count;
}
}
[Leetcode]2212. 射箭比赛中的最大得分
Alice 和 Bob 是一场射箭比赛中的对手。比赛规则如下:
Alice 先射 numArrows 支箭,然后 Bob 也射 numArrows 支箭。
分数按下述规则计算:
箭靶有若干整数计分区域,范围从 0 到 11 (含 0 和 11)。
箭靶上每个区域都对应一个得分 k(范围是 0 到 11),Alice 和 Bob 分别在得分 k 区域射中 ak 和 bk 支箭。如果 ak >= bk ,那么 Alice 得 k 分。如果 ak < bk ,则 Bob 得 k 分
如果 ak == bk == 0 ,那么无人得到 k 分。
例如,Alice 和 Bob 都向计分为 11 的区域射 2 支箭,那么 Alice 得 11 分。如果 Alice 向计分为 11 的区域射 0 支箭,但 Bob 向同一个区域射 2 支箭,那么 Bob 得 11 分。
给你整数 numArrows 和一个长度为 12 的整数数组 aliceArrows ,该数组表示 Alice 射中 0 到 11 每个计分区域的箭数量。现在,Bob 想要尽可能 最大化 他所能获得的总分。
返回数组 bobArrows ,该数组表示 Bob 射中 0 到 11 每个 计分区域的箭数量。且 bobArrows 的总和应当等于 numArrows 。
如果存在多种方法都可以使 Bob 获得最大总分,返回其中 任意一种 即可。
二进制枚举或者动态规划。 考虑到这道题numArrows可能会很大,而枚举只有\(2^{12}-1\)种可能,二进制枚举算法更优。
class Solution {
// 二进制枚举
public int[] maximumBobPoints(int numArrows, int[] aliceArrows) {
int maxScore = 0, maxNeed = 0;
int plan = 0;
int[] res = new int[12];
for(int i=1;i<Math.pow(2, 13);++i) {
int need = 0, score = 0;
for(int j=0;j<12;++j) {
if((i & (1<<j))!=0){
need += aliceArrows[j]+1;
score += j;
}
}
if(need <= numArrows && score > maxScore) {
maxScore = score;
maxNeed = need;
plan = i;
}
}
for(int i=0;i<12;++i) {
if((plan & (1<<i))!=0) res[i] = aliceArrows[i]+1;
}
res[0] += numArrows - maxNeed;
return res;
}
// 动态规划
public int[] maximumBobPoints(int numArrows, int[] aliceArrows) {
int[][] dp = new int[12][numArrows+1];
int[] res = new int[12];
for(int i=1;i<12;++i) {
for(int num=0;num<numArrows+1;++num) {
if(num>aliceArrows[i]) dp[i][num] = Math.max(dp[i-1][num], dp[i-1][num-aliceArrows[i]-1]+i);
else dp[i][num] = dp[i-1][num];
}
}
for(int i=11;i>=1;--i) {
if(dp[i][numArrows] > dp[i-1][numArrows]) {
res[i] = aliceArrows[i]+1;
numArrows -= aliceArrows[i]+1;
}
}
res[0] += numArrows;
return res;
}
}
[Leetcode]2213. 由单个字符重复的最长子字符串
给你一个下标从 0 开始的字符串 s 。另给你一个下标从 0 开始、长度为 k 的字符串 queryCharacters ,一个下标从 0 开始、长度也是 k 的整数 下标 数组 queryIndices ,这两个都用来描述 k 个查询。
第 i 个查询会将 s 中位于下标 queryIndices[i] 的字符更新为 queryCharacters[i] 。
返回一个长度为 k 的数组 lengths ,其中 lengths[i] 是在执行第 i 个查询 之后 s 中仅由 单个字符重复 组成的 最长子字符串 的 长度 。
一道经典的线段树应用题。使用线段树求解,我们唯一需要考虑的是:在 Node 中维护些什么信息?
对于线段树的节点信息设计,通常会包含基本的左右端点 l、r 以及查询目标值 val ,然后再考虑维护 val 还需要一些什么辅助信息。
对于本题,我们还需要额外维护 prefix 和 suffix,分别代表「当前区间 [l, r][l,r] 内前缀相同字符连续段的最大长度」和「当前区间 [l, r][l,r] 内后缀相同字符连续段的最大长度」。
class Solution {
class Node {
int l, r, prefix, suffix, val;
Node(int _l, int _r) {
l = _l; r = _r;
prefix = suffix = val = 1;
}
}
char[] cs;
Node[] tr;
void build(int u, int l, int r) {
tr[u] = new Node(l, r);
if (l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
void update(int u, int x, char c) {
if (tr[u].l == x && tr[u].r == x) {
cs[x - 1] = c;
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) update(u << 1, x, c);
else update(u << 1 | 1, x, c);
pushup(u);
}
int query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) return tr[u].val;
int ans = 0;
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) ans = query(u << 1, l, r);
if (r > mid) ans = Math.max(ans, query(u << 1 | 1, l, r));
return ans;
}
void pushup(int u) {
Node left = tr[u << 1], right = tr[u << 1 | 1];
int aLen = left.r - left.l + 1, bLen = right.r - right.l + 1;
char ac = cs[left.r - 1], bc = cs[right.l - 1];
tr[u].prefix = left.prefix; tr[u].suffix = right.suffix;
tr[u].val = Math.max(left.val, right.val);
if (ac == bc) {
if (left.prefix == aLen) tr[u].prefix = aLen + right.prefix;
if (right.prefix == bLen) tr[u].suffix = bLen + left.suffix;
tr[u].val = Math.max(tr[u].val, left.suffix + right.prefix);
}
}
public int[] longestRepeating(String s, String queryCharacters, int[] queryIndices) {
cs = s.toCharArray();
int n = cs.length, m = queryCharacters.length();
tr = new Node[n * 4];
build(1, 1, n);
for (int i = 0; i < n; i++) update(1, i + 1, cs[i]);
int[] ans = new int[m];
for (int i = 0; i < m; i++) {
update(1, queryIndices[i] + 1, queryCharacters.charAt(i));
ans[i] = query(1, 1, n);
}
return ans;
}
}