「LeetCode43」字符串相乘|刷题打卡

162 阅读3分钟

这是 LeetCode 第 43 题:

给定两个以字符串形式表示的非负整数 a 和 b,返回 a 和 b 的乘积,它们的乘积也表示为字符串形式。

初次看到此题,真是一点思路都没有,但是想到所有乘法本质上都是加法运算,那能否先转化成字符串相加的题目呢?即:

给定两个以字符串形式表示的非负整数 a 和 b,返回 a 和 b 的和,它们的和也表示为字符串形式。

这样就稍微有点思路了,步骤如下:

  1. 把两个需要相加的字符串位数凑成一样的
  2. 从后往前一位一位的加,如果有进位的话,就记下来,下一轮加上
  3. 别忘了单独处理首位相加进位的情况

代码如下:

function add(a, b) {
  const len = Math.max(a.length, b.length) // 获取最大长度
  a = a.padStart(len, '0') // 对齐
  b = b.padStart(len, '0') // 对齐
  let sum = '' // 两数之和
  let overflow = 0 // 记录是否溢出
  for (let i = len - 1; i >= 0; i--) { // 从最后一位往前计算
    let ai = a.charAt(i) // 取出字符串 a 的第 i 位的值
    let bi = b.charAt(i) // 取出字符串 b 的第 i 位的值
    let val = +ai + +bi + overflow // 转成数字相加,同时要加上溢出位的值
    if (val >= 10) { // 如果溢出就要取末尾,然后设置溢出为 1
      val = val % 10
      overflow = 1
    } else {
      overflow = 0 // 没有溢出就设置为 0
    }
    sum = val + sum // 把当前计算结果放到 sum 字符串的最前面,就是新的 sum
  }
  if (overflow) sum = '1' + sum // 特殊处理首位溢出的情况
  return sum
}

由于加了详细注释,这里就不逐行解释了,可以写一段脚本验证一下:

function checkAdd(n = 10000, bound = 1000000) {
  for (let i = 0; i < n; i++) {
    const a = (Math.random() * bound) | 0
    const b = (Math.random() * bound) | 0
    const v = `${a + b}`
    const s = add(`${a}`, `${b}`)
    if (v !== s) {
      console.log(`${a}+${b}=${s}计算有误,正确答案是${v}`)
      return
    }
  }
  console.log('都对了')
}
checkAdd()

顺利通过测试。

what-then

有了加法的基础之后,乘法就可以转换成加法了,例如 53*78 不就是 53 个 78 相加吗?于是我写出了如下的代码:

function mul(a, b) {
  let sum = '0'
  for (let i = 0; i < +a; i++) sum = add(sum, b)
  return sum
}

然而,被 mul('123456789', '987654321') 直接搞超时了... ,看来思路不对。最终还是看了题解,觉得非常巧妙,关键下面这句话:

m 位数与 n 位数的乘积不会超过 m+n 位数,所以可以定义一个长度为 m+n 的数组,a[i]b[j] 的乘积对应的就是 arr[i+j]arr[i+j+1] 这两个位置。

AC 代码如下:

function mul(a, b) {
  const aLen = a.length // 取 a 的长度
  const bLen = b.length // 取 b 的长度
  const arr = Array.from({length: aLen + bLen}, () => 0) // 定义长度为 a+b 的数组,全部初始化为 0
  for (let i = aLen - 1; i >= 0; i--) { // 从后往前遍历 a
    const ai = +a.charAt(i) // 记录 a 的第 i 位的数值
    for (let j = bLen - 1; j >= 0; j--) { // 从后往前遍历 b
      const bj = +b.charAt(j) // 记录 b 的第 j 位的数值
      const product = ai * bj // 记录 a 的第 i 位与 b 的第 j 位的乘积
      const sum = arr[i + j + 1] + product // 求和
      arr[i + j + 1] = sum % 10 // 只保留最后一位
      arr[i + j] += (sum / 10) | 0 // 如果溢出了,留给前一位相加用
    }
  }
  while (arr[0] === 0) arr.shift() // 处理前面多个 0 的情况
  return arr.join('') || '0' // 转换成字符串
}

AC 截图如下:

string-mul

本文正在参与「掘金 3 月闯关活动」,点击查看活动详情