前言
字符串乘法,也即大数相乘,为一道模拟题。此题既考察如何模拟乘?同时考察如何模拟大数加法?除此之外,大数的高位/低位在左边还是右边,这是里面的一处细节,可以延申出很多bug。
一、字符串乘法
二、乘法&加法的模拟
package everyday.medium;
// 字符串相乘
public class Multiply {
/*
字符串相乘,不能直接乘,所以需要进行乘法模拟。
乘法的步骤:一个数的每一位乘上另一个数,会得到一个数,然后做一个大数加法即可。
要做大数加法,就得有个整数数组,那么数组的长度是多少呐?
将最大一位数9放大最大两位数99,即9 * 99 < 10 * 99 = 990;所以两数相乘,必不可能超过两者的长度。
同理:10 * 100 = 1000,最小也是两者长度-1,即最多包含一个前导0.(除乘0的特殊情况)。
*/
// review:
// bug1:对于字符串,从左到右是,先高位再低位,所以做乘法时应该从右往左边遍历。
// bug2:对于ans数组,从左到右是,先低位再高位,所以转成字符串时应该从右向左遍历。
// bug3:忽略了+和三元运算符的优先级关系,+ > ?,好久没碰到都忘了,以前错了一次,记忆还是多深刻的。
// bug4:先低位乘再高位,而字符串是从高位到低位,所以应该倒着来。
// bug5:第二个数为0,则会出现很多0的情况,提前判断了。
// 总:两个bug都体现了对细节的把握度不够,没有认真实践去看数字怎么排列,而是好像见过就自以为去了。
public String multiply(String num1, String num2) {
int m = num1.length(), n = num2.length();
int[] ans = new int[m + n];
// bug5:第二个数为0,则会出现很多0的情况,提前判断了。
if (num1.length() == 1 && num1.charAt(0) == '0' ||
num2.length() == 1 && num2.charAt(0) == '0') return "0";
// bug4:先低位乘再高位,而字符串是从高位到低位,所以应该倒着来。
for (int i = num1.length() - 1; i >= 0; i--) {
int k = num1.charAt(i) - '0';
if (k == 0) continue;
String s = multiply(k, num2);
add(s, num1.length() - 1 - i, ans);
}
StringBuilder sb = new StringBuilder();
// bug2:结果从左到右是高位到低位,弄反了。
for (int i = ans.length - 1; i >= 0; i--) {
if (i == ans.length - 1 && ans[i] == 0) continue;
sb.append(ans[i]);
}
return sb.toString();
}
private void add(String s, int offset, int[] ans) {
int plus = 0;
for (int i = offset; i < ans.length; i++) {
// bug3:忽略了+和三元运算符的优先级关系,+ > ?
int val = ans[i] + (i - offset < s.length() ? s.charAt(i - offset) - '0' : 0) + plus;
ans[i] = val % 10;
plus = val / 10;
}
}
private String multiply(int m, String s) {
int plus = 0;
StringBuilder sb = new StringBuilder();
// bug1:注意模拟时,先低位再高位。
for (int i = s.length() - 1; i >= 0; i--) {
int val = (s.charAt(i) - '0') * m + plus;
plus = val / 10;
sb.append(val - plus * 10);
}
if (plus != 0) sb.append(plus);
return sb.toString();
}
public static void main(String[] args) {
new Multiply().multiply("123", "456");
}
}
总结
1)大数乘法,同时考察字符串的乘法&字符串的加法。
2)对于模拟数乘/数加,细节的本质在于高位/低位是在左还是在右,如果不细心校验,这可以引出很多bug。
参考文献
[1] LeetCode 字符串乘法