跳转至

字符串乘法⚓︎

模拟笔算乘法⚓︎

对于长为 mn 的整数字符串 num1num2,考虑第 i 位和第 j 位的乘积,产生乘积个位应该落在索引 i+j+1 处,十位在索引 i+j 处。
再考虑进位,那么应该将索引 i+j+1 处置为 (num1[i]*num2[j]+carry) \% 10,然后索引 i+j 处记上进位 (num1[i]*num2[j]+carry) / 10

模拟笔算
C++
std::string multiply(const std::string &num1, const std::string &num2) {
  int m = num1.size(), n = num2.size();
  if (num1 == "0" || num2 == "0") { return "0"; }
  std::vector<int> res(m + n, 0);
  for (int i = m - 1; i >= 0; i--) {
    for (int j = n - 1; j >= 0; j--) {
      // 倒序相乘,两个循环
      int mul = (num1[i] - '0') * (num2[j] - '0');
      int p1 = i + j, p2 = i + j + 1;
      int sum  = mul + res[p2];
      res[p2]  = sum % 10;
      res[p1] += sum / 10;
    }
  }
  // 或者直接用string流(stringstream)来拼接
  // 注意跳过前导0
  std::string result;
  for (int num : res) {
    if (!(result.empty() && num == 0)) { result.push_back(num + '0'); }
  }
  return result.empty() ? "0" : result;
}

逆向考虑⚓︎

对于结果中的第 i 位(假设从右数起),由所有满足 j+q=i 的位相乘后相加得到(再加上 i-1 位的进位)。

逆向
C++
#include <algorithm>

std::string multiply(const string &num1, const string &num2) {
  // 注意这里m,n和上面说明的字符串长度不一样,方便计算下标
  int m = num1.length() - 1, n = num2.length() - 1, carry = 0;
  std::string product;
  for (int i = 0; i <= m + n || carry; ++i) {
    // 只需考虑m+n位,carry是为了考虑最后一些进位
    for (int j = max(0, i - n); j <= min(i, m); ++j){
      // 从右起第j位对应的下标就是m-j;计算q=i-j,对应坐标为n-(i-j)
      carry += (num1[m - j] - '0') * (num2[n - i + j] - '0');
    }
    product += carry % 10 + '0';
    carry   /= 10;  // 保留进位
  }
  std::reverse(product.begin(), product.end());
  return product;
}

考虑 j 的取值范围: j 可以从 0 开始,但也要满足加上 q 的最大值 n 后能达到 i ,也即至少为 i-n,因此 i 应该取二者的最大值开始;j 不能超过 i,也不能超过 num1 本身长度。

评论