leetcode 374场周赛总结
找出峰值
这一题比较简单,只需要遍历数组的第二个元素到倒数第二个元素,然后遍历的过程中判断元素大于前后两个元素就行。
var findPeaks = function (mountain) {
const result = [];
for (let i = 1; i < mountain.length - 1; i++) {
if (mountain[i] > mountain[i - 1] && mountain[i] > mountain[i + 1]) {
result.push(i);
}
}
return result;
};
需要添加的硬币的最小数量
为方便描述,把 000 也算作可以得到的数。
假设现在得到了 [0,s−1][0,s-1][0,s−1] 内的所有整数,如果此时新发现了一个整数 xxx,那么把 xxx 加到已得到的数字中,就得到了 [x,s+x−1][x,s+x-1][x,s+x−1] 内的所有整数。
分类讨论:
如果 x≤sx \le sx≤s,那么合并这两个区间,我们可以得到 [0,s+x−1][0,s+x-1][0,s+x−1] 内的所有整数。
如果 x>sx > sx>s,这意味着我们无法得到 sss,那么就一定要把 sss 加到数组中(加一个比 sss 还小的数字就没法得到更大的数,不够贪),这样就可以得到 [s,2s−1][s,2s-1][s,2s−1] 内的所有整数,再与 [0,s−1][0,s-1][0,s−1] 合并,可以得到 [0,2s−1][0,2s-1][0,2s−1] 内的所有整数。然后再重新考虑 xxx 和 sss 的大小关系,继续分类讨论。
把 coins\textit{coins}coins 排序,从小到大考虑 x=coins[i]x=\textit{coins}[i]x=coins[i]。按照上述分类讨论来看是否要添加数字。
var minimumAddedCoins = function (coins, target) {
coins.sort((x, y) => x - y);
let ans = 0,
// 一开始只能组成[0, S-1]的所有数
s = 1,
i = 0;
// 当s-1>=target的时候就表示[1, target]都有了
while (s <= target) {
if (i < coins.length && coins[i] <= s) {
s += coins[i]; // [0, s+coins[i]-1]的所有数
i += 1;
} else {
// 必须要添加一个数
s *= 2;
ans += 1;
}
}
return ans;
};
统计完全子字符串
这一题我们首先看这一个条件“相邻字符在字母表中的顺序 至多 相差 2 。也就是说,s 中两个相邻字符 c1 和 c2 ,它们在字母表中的位置相差 至多 为 2”,如果相差大于2了这个字符串就不连续了,我们第一步只需要通过“分组循环”将字符串分割成多个连续的字符串。
然后在多个连续的字符串分别使用滑动窗口判断另外一个条件“s 中每个字符 恰好 出现 k 次”即可。
var countCompleteSubstrings = function (word, k) {
// 分组循环
let n = word.length;
let i = 0;
let ans = 0;
while (i < n) {
let start = i;
i++;
while (
i < n &&
Math.abs(word.charCodeAt(i) - word.charCodeAt(i - 1)) <= 2
) {
i++;
}
// s中有多少个字串,其每个字符恰好出现k次
ans += solve(word.substring(start, i), k);
}
return ans;
};
// 滑动窗口
function solve(s, k) {
let ans = 0;
for (let m = 1; m < 27; m++) {
let size = m * k;
if (size > s.length) break;
let cnt = new Array(26).fill(0);
for (let n = 0; n < s.length; n++) {
cnt[s.charCodeAt(n) - 97]++;
const left = n - size + 1;
if (left >= 0) {
ans += check(cnt, k);
cnt[s.charCodeAt(left) - 97]--;
}
}
}
return ans;
}
// 计算是否每个字符恰好出现k次
function check(cnt, k) {
for (let i = 0; i < cnt.length; i++) {
if (cnt[i] !== 0 && cnt[i] !== k) {
return 0;
}
}
return 1;
}
上述代码用了两个比较经典的算法思想,“分组循环”和”滑动窗口“。
”分组循环“
let arr = "储存分割后的元素";
let i = "开始索引";
let n = "长度"
while (i < n) {
let start = i;
i++;
while (
i < n &&
"不符合的条件"
) {
i++;
}
ans += "从start到i-1进行分割"
}
"滑动窗口"
function solve(s, k) {
let ans = 0;
for (let m = 1; m < 27; m++) {
let size = m * k;
if (size > s.length) break;
let cnt = new Array(26).fill(0);
for (let n = 0; n < s.length; n++) {
cnt[s.charCodeAt(n) - 97]++;
const left = n - size + 1;
if (left >= 0) {
ans += check(cnt, k);
cnt[s.charCodeAt(left) - 97]--;
}
}
}
return ans;
}