leetcode 374场周赛总结

64 阅读3分钟

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