js解leetcode(59)-中等

1.分汤

题目:

有 A 和 B 两种类型的汤。一开始每种类型的汤有 N 毫升。有四种分配操作:

提供 100ml 的汤A 和 0ml 的汤B。
提供 75ml 的汤A 和 25ml 的汤B。
提供 50ml 的汤A 和 50ml 的汤B。
提供 25ml 的汤A 和 75ml 的汤B。
当我们把汤分配给某人之后,汤就没有了。每个回合,我们将从四种概率同为0.25的操作中进行分配选择。如果汤的剩余量不足以完成某次操作,我们将尽可能分配。当两种类型的汤都分配完时,停止操作。

注意不存在先分配100 ml汤B的操作。

需要返回的值: 汤A先分配完的概率 + 汤A和汤B同时分配完的概率 / 2。

思路:动态规划,dp[i][j] 表示 A 的汤为i, B 的汤为j 的时候,结果的概率。 状态转移方程

dp[i][j] = 0.25 * (dp[i-4][j] + dp[i-1][j-1] + dp[i-2][j-2] + dp[i-1][j-3]);
初始条件 dp[0][0] = 0.5; dp[0]j = 1; dp[i]0 = 0;
因为每次都是25ml 增加和减少,所以可以把 N / 25 来处理,当无法整除的时候,要加1,因为 多出来那部分,必须进行到下一轮下一部分

时间复杂度O(1),空间复杂度O(1)。因为最多500次

/**
 * @param {number} N
 * @return {number}
 */
var soupServings = function(N) {
  let num = Math.ceil(N / 25);
  if (num >= 500) {
    return 1.0;
  }
  const dp = new Array(num + 1).fill("").map(() => new Array(num + 1).fill(0));
  dp[0][0] = 0.5;
  for (let i = 1; i <= num; i++) {
    dp[0][i] = 1;
  }
  for (let i = 1; i <= num; i++) {
    for (let j = 1; j <= num; j++) {
      dp[i][j] =
        0.25 *
        (dp[i - 4 > 0 ? i - 4 : 0][j] +
          dp[i - 3 > 0 ? i - 3 : 0][j - 1 > 0 ? j - 1 : 0] +
          dp[i - 2 > 0 ? i - 2 : 0][j - 2 > 0 ? j - 2 : 0] +
          dp[i - 1 > 0 ? i - 1 : 0][j - 3 > 0 ? j - 3 : 0]);
    }
  }
  return dp[num][num];

};

2.情感丰富的文字

题目:

有时候人们会用重复写一些字母来表示额外的感受,比如 "hello" -> "heeellooo", "hi" -> "hiii"。我们将相邻字母都相同的一串字符定义为相同字母组,例如:"h", "eee", "ll", "ooo"。

对于一个给定的字符串 S ,如果另一个单词能够通过将一些字母组扩张从而使其和 S 相同,我们将这个单词定义为可扩张的(stretchy)。扩张操作定义如下:选择一个字母组(包含字母 c ),然后往其中添加相同的字母 c 使其长度达到 3 或以上。

例如,以 "hello" 为例,我们可以对字母组 "o" 扩张得到 "hellooo",但是无法以同样的方法得到 "helloo" 因为字母组 "oo" 长度小于 3。此外,我们可以进行另一种扩张 "ll" -> "lllll" 以获得 "helllllooo"。如果 S = "helllllooo",那么查询词 "hello" 是可扩张的,因为可以对它执行这两种扩张操作使得 query = "hello" -> "hellooo" -> "helllllooo" = S。

输入一组查询单词,输出其中可扩张的单词数量。

思路:双指针。字符串要扩张得到目标字符串,条件有两个,一个是字符的相对顺序要一样。因为只能复制字符,不能新增字符。二是连续的字符长度如果不相同时,一定要大于等于3
所以可以双指针按顺序比较,然后记录当前这个相连的相同字符数量l1和l2.
l1表示S的相同相连字符数量,l2表示数组中当前字符串的相同相连字符数量
在遍历时,首先比较当前位置的字符是否相同,不同则直接返回false;、
然后得到当前的l1和l2,如果l2>l1,那么也是直接返回false
如果l1!==l2且l1===2,说明不能满足大于等于3,也是返回false
其他情况则可以扩张,进入下一轮遍历
时间复杂度O(n*(max(m,k))),空间复杂度O(1) n是数组长度,m和k是S的长度和数组中单词长度的最大值

/**
 * @param {string} S
 * @param {string[]} words
 * @return {number}
 */
var expressiveWords = function(S, words) {
  const l = S.length;
  return words.filter((item) => {
    const s1 = item.length;
    if (s1 >= l) return false;
    let i1 = 0,
      i2 = 0;
    let l1, l2;
    while (i1 < l && i2 < s1) {
      l1 = 1;
      l2 = 1;
      if (S[i1] !== item[i2]) return false;
      while (S[i1] === S[i1 + 1]) {
        i1++;
        l1++;
      }
      while (item[i2] === item[i2 + 1]) {
        i2++;
        l2++;
      }
      if (l2 > l1) return false;
      if (l1 !== l2 && l1 === 2) return false;
      i1++;
      i2++;
      continue;
    }
    if (i1 !== l || i2 !== s1) return false;
    return true;
  }).length;
};

3.最大平均值的和分组

题目:

我们将给定的数组 A 分成 K 个相邻的非空子数组 ,我们的分数由每个子数组内的平均值的总和构成。计算我们所能得到的最大分数是多少。

注意我们必须使用 A 数组中的每一个数进行分组,并且分数不一定需要是整数。

思路:

建立dp数组,dp[i][j]表示分为i组时,0-j的元素的平均值的最大和
那么对于所有的i<=m<j,有
dp[i][j]=max(dp[i][j],dp[i-1][m]+(∑(m->j)/(j-m)),
也就是分组少一次时的结果加上后续剩余元素的平均值,的最大值

时间复杂度O(m*n2),空间复杂度O(mn)

/**
 * @param {number[]} A
 * @param {number} K
 * @return {number}
 */
var largestSumOfAverages = function(A, K) {
  const l = A.length;
  const sum = new Array(l).fill(0);
  sum[0] = A[0];
  for (let i = 1; i < l; i++) {
    sum[i] = sum[i - 1] + A[i];
  }
  const dp = new Array(K + 1).fill("").map(() => new Array(l).fill(0));
  for (let i = 0; i < l; i++) {
    dp[1][i] = sum[i] / (i + 1);
  }
  for (let i = 1; i <= K; i++) {
    dp[i][i - 1] = sum[i - 1];
  }
  for (let i = 2; i <= K; i++) {
    for (let j = i; j < l; j++) {
      for (let m = j - 1; m >= i - 2; m--) {
        dp[i][j] = Math.max(
          dp[i][j],
          dp[i - 1][m] + (sum[j] - sum[m]) / (j - m)
        );
      }
    }
  }
  return dp[K][l - 1];
};

4.二叉树剪枝

题目:

给定二叉树根结点 root ,此外树的每个结点的值要么是 0,要么是 1。

返回移除了所有不包含 1 的子树的原二叉树。

( 节点 X 的子树为 X 本身,以及所有 X 的后代。)

思路:dfs。两种方式:dfs的函数可以返回当前子树的非0节点的数量,然后判断左右子树如果非0节点数量是0,那么子树为null,

时间复杂度O(n) 空间复杂度O(h)

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var pruneTree = function(root) {
  const dfs = (root) => {
    if (!root) return 0;
    const left = dfs(root.left);
    const right = dfs(root.right);
    if (!left) {
      root.left = null;
    }
    if (!right) {
      root.right = null;
    }
    return left + right + (root.val === 0 ? 0 : 1);
  };
  dfs(root);
  if (!root.val && !root.left && !root.right) return null;
  return root;
};

也可以直接返回结果。如果左右结果是null且当前节点是0,那么当前节点返回null

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var pruneTree = function(root) {
  const dfs = (root) => {
    if (!root) return null;
    root.left = dfs(root.left);
    root.right = dfs(root.right);
    if (!root.val && !root.left && !root.right) return null;
    return root;
  };
  return dfs(root);
};

5.模糊坐标

题目:

我们有一些二维坐标,如 "(1, 3)" 或 "(2, 0.5)",然后我们移除所有逗号,小数点和空格,得到一个字符串S。返回所有可能的原始字符串到一个列表中。

原始的坐标表示法不会存在多余的零,所以不会出现类似于"00", "0.0", "0.00", "1.0", "001", "00.01"或一些其他更小的数来表示坐标。此外,一个小数点前至少存在一个数,所以也不会出现“.1”形式的数字。

最后返回的列表可以是任意顺序的。而且注意返回的两个数字中间(逗号之后)都有一个空格。

思路:分治法。先分成左右两部分,然后分别枚举小数点的位置

时间复杂度O(n3)空间复杂度O(n3)

/**
 * @param {string} S
 * @return {string[]}
 */
var ambiguousCoordinates = function(S) {
  const ans = []
  const len = S.length
  for (let i = 2; i < len; i++) {
    const left = S.substring(1, i)
    const right = S.substring(i, len - 1)
    if ((left.length > 1 && +left === 0) || (right.length > 1 && +right === 0)) continue
    for (let num1 of getNums(left)) {
      for (let num2 of getNums(right)) {
        ans.push('(' + num1 + ', ' + num2 + ')')
      }
    }
  }
  return ans

  function getNums(str) {
    const len = str.length
    const set = new Set()
    if (len === 1) {
      set.add(parseInt(str))
    } else {
      for (let i = 1; i <= len; i++) {
        const left = str.substring(0, i)
        const right = str.substring(i)
        if ((+left + '').length !== left.length) break
        if ((+right === 0 && right.length !== 0) || right[right.length - 1] === '0') continue
        const num1 = left + '.' + right
        set.add(+num1)
      }
    }
    return set
  }

};

 

上一篇:剑指 Offer 59 - II. 队列的最大值(单调队列)


下一篇:5.1.15 MySQL 服务器时区支持