[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;
}
}