每天一道算法题 - 第一天

315 阅读3分钟

题目

来源:LeetCode 299 猜数字游戏(中等)

描述:

你在和朋友一起玩猜数字(Bulls and Cows)游戏,该游戏规则如下:
写出一个秘密数字,并请朋友猜这个数字是多少。朋友每猜测一次,你就会给他一个包含下述信息的提示:
    - 猜测数字中有多少位属于数字和确切位置都猜对了(称为 "Bulls", 公牛)
    - 有多少位属于数字猜对了但是位置不对(称为 "Cows", 奶牛)。也就是说,这次猜测中有多少位非公牛数字可以通过重新排列转换成公牛数字。
给你一个秘密数字 secret 和朋友猜测的数字 guess ,请你返回对朋友这次猜测的提示。
提示的格式为 "xAyB" ,x 是公牛个数, y 是奶牛个数,A 表示公牛,B 表示奶牛。
请注意秘密数字和朋友猜测的数字都可能含有重复数字。

示例 1

输入: secret = "1807", guess = "7810"
输出: "1A3B"
解释: 数字和位置都对(公牛)用 '|' 连接,数字猜对位置不对(奶牛)的采用'x'标识。
"1807"
 x|xx
"7810"

示例 2

输入: secret = "1123", guess = "0111"
输出: "1A1B"
解释: 数字和位置都对(公牛)用 '|' 连接,数字猜对位置不对(奶牛)的采用'x'标识。
"1123"        "1123"
  |x      or    | x
"0111"        "0111"
注意,两个不匹配的 1 中,只有一个会算作奶牛(数字猜对位置不对)。通过重新排列非公牛数字,其中仅有一个 1 可以成为公牛数字。

示例 3

输入:secret = "1", guess = "0"
输出:"0A0B"

示例 4

输入:secret = "1", guess = "1"
输出:"1A0B"

解析

题目解读: 传入两个数,判断其值相等但位不等的个数、值和位都相等的个数并按"xAyB"的格式返回,其中"x"为值和位都相等的个数,"y"为值相等,但位不相等。

注:如果在 guess 中有两个值等于 secret 中的同一个值,只取其中一次

思路: 遍历

  • 创建俩个长度为10的数组,初始值为0,分别保存 secret 及 guess 中,在不是公牛的情况下,各个数字出现的次数
  • 遍历字符串,判断俩字符串当前索引的字符值是否相等
  • 相等的话公牛数加一,不相等的话在各自的统计数组中记录
  • 遍历统计数组,取小值相加计算母牛数
  • 时间复杂度:O(n),空间复杂度:O(1)

代码

/**
 * @param {string} secret
 * @param {string} guess
 * @return {string}
 */
var getHint = function(secret, guess) {
  let bulls = 0; // 公牛数
  let cows = 0; // 母牛数
  const sNums = new Array(10).fill(0); // secret 字符串中,非公牛数字出现的次数统计数组
  const gNums = new Array(10).fill(0); // guess 字符串中,非公牛数字出现的次数统计数组
  for (let i = 0; i < secret.length; i++) {
    const sc = parseInt(secret.charAt(i));
    const gc = parseInt(guess.charAt(i));
    if (sc === gc) { // 公牛
      bulls++;
    } else { // 将当前值统计至数组
      sNums[sc]++;
      gNums[gc]++;
    }
  }
  // 统计母牛数
  for (let i = 0; i < sNums.length; i++) {
    cows += Math.min(sNums[i], gNums[i]);
  }
  return `${bulls}A${cows}B`;
};

优化:在上述思路的基础上,对代码层面的优化。在时间和空间复杂度上,没有太大变化

/**
 * @param {string} secret
 * @param {string} guess
 * @return {string}
 */
var getHint = function(secret, guess) {
  let bulls = 0; // 公牛数
  let cows = 0; // 母牛数
  const nums = new Array(10).fill(0); // secret 字符串中,各数字出现的次数统计数组
  for (let i = 0; i < secret.length; i++) {
    const sc = parseInt(secret.charAt(i));
    const gc = parseInt(guess.charAt(i));
    if (sc === gc) { // 公牛
      bulls++;
    } else { // guess 以负值统计,secret 以正值统计
      if (nums[gc]-- > 0) { // gc 之前在 secret 中出现过得话,就代表 gc 是母牛
        cows++;
      }
      if (nums[sc]++ < 0) { // sc 之前在 guess 中出现过得话,就代表 sc 是母牛
        cows++;
      }
    }
  }
  return `${bulls}A${cows}B`;
};