题目
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
示例 1:
输入: num1 = “2”, num2 = “3”
输出: “6”
示例 2:
输入: num1 = “123”, num2 = “456”
输出: “56088”
说明:
- num1 和 num2 的长度小于110。
- num1 和 num2 只包含数字 0-9。
- num1 和 num2 均不以零开头,除非是数字 0 本身。
- 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
解答
解法一:平行乘法
这是最直观的解法,最符合我们平时计算乘法的方式。
分为以下几步:
- 从低到高位计算出每一行的乘积字符串。
- 将计算出的每一行乘积字符串补上合适个数的 0 ,并保存起来。
- 对所有的乘积字符串进行字符串加法运算。(融合操作类似于合并 k 个有序链表,可以自行优化或博客)
代码挺长,但很简单。
深入学习这种解法也能学到不少东西,包括字符串加法,合并 k 个有序链表的应用。
代码
class Solution {
public String multiply(String num1, String num2) {
if(num1 == null || num1.length() == 0) return "0";
if("0".equals(num1) || "0".equals(num2)) return "0";
String[] results = new String[num1.length()];
for(int i = num1.length() - 1; i >= 0; i --) {
int n1Val = num1.charAt(i) - '0';
int carry = 0;
StringBuilder cur = new StringBuilder();
for(int j = num2.length() - 1; j >= 0; j --) {
int n2Val = num2.charAt(j) - '0';
int sum = carry + n1Val * n2Val;
carry = sum / 10;
cur.append(sum % 10);
}
cur.append(carry != 0 ? carry : "");
results[i] = cur.reverse().append(generateZero(num1.length() - i - 1)).toString();
}
// 这一部分融合操作可以优化成 分治 算法,效率更高。
// 类似于合并 k 个有序链表。
// 这里简单起见,我并没有优化,感兴趣的同学可以做一下优化。
String res = results[0];
for(int i = 1; i < results.length; i ++) {
res = addStrings(res, results[i]);
}
return res;
}
private String generateZero(int n) {
char[] zeros = new char[n];
Arrays.fill(zeros, '0');
return new String(zeros);
}
private String addStrings(String num1, String num2) {
StringBuilder res = new StringBuilder();
int carry = 0;
int i = num1.length() - 1;
int j = num2.length() - 1;
while(i >= 0 || j >= 0 || carry != 0) {
int num1Val = i >= 0 ? num1.charAt(i) - '0' : 0;
int num2Val = j >= 0 ? num2.charAt(j) - '0' : 0;
int sum = carry + num1Val + num2Val;
carry = sum / 10;
res.append(sum % 10);
if(i >= 0) i --;
if(j >= 0) j --;
}
return res.reverse().toString();
}
}
结果
解法二:竖式乘法
首先需要知道一点:两个数字相乘,最后的结果的长度必然小于两者的长度之和。
竖式乘法就是采用的这种思想,相当于将结果整个采用数组的方式存储。
手画了一个简单的图,画的粗糙,见谅 :)
图中结果数组中的第 k 位在哪些情况下会被修改呢?
现在我们假设 num1 当前的位置是 i ,num2 当前的位置是 j 。
那么可以看出:i + j + 1 == k 时第 k 位会被直接修改,同时也可能会进位第 k - 1 位。
具体情况如下:
- 结果的 第 k 位 上的值就是(原来第 k 位的值 ret[k] + 增量 num1[i] * num2[j] )之和的余数。
- 结果的 第 k - 1 位,可能会得到 ret[k] + num1[i] * num2[j] 运算产生的进位。
代码
class Solution {
public String multiply(String num1, String num2) {
char[] ret = new char[num1.length() + num2.length()];
Arrays.fill(ret, '0');
for(int i = num1.length() - 1; i >= 0; i --) {
int num1Val = num1.charAt(i) - '0';
for(int j = num2.length() - 1; j >= 0; j --) {
int num2Val = num2.charAt(j) - '0';
int sum = (ret[i + j + 1] - '0') + num1Val * num2Val;
ret[i + j + 1] = (char)(sum % 10 + '0');
ret[i + j] += sum / 10;
}
}
for(int i = 0; i < ret.length; i ++) {
if(ret[i] != '0' || i == ret.length - 1) {
return new String(ret, i, ret.length - i);
}
}
return "0";
}
}