给定两个以字符串形式表示的非负整数 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)或直接将输入转换为整数来处理。
题目分析
//1 2 3 num1 //4 5 6 num2 //7 3 8+6 1 5 * 10+..... reverseresult
1. 竖式暴力模拟
因为现实竖式乘法是从右到左落位,
程序从左到右方便一些,所以运算过程中是反向存储的乘法结果,最后需要翻转.
当然这个可以优化
1.1
整数相乘直接思维是按竖式相乘, 显而易见两次循环,外层num2,内层num1
1.2
对num2的最后一位,乘num1一遍,currentresult保存这遍的结果
1.3
result累加上currentresult, (加法也有点麻烦, 还得写个加法函数
1.4
因为最后一位乘过了就没用了,num2.pop_back()
1.5
注意乘完一个数, 下一个currentresult要向左移一位,即X10尾部添0,
而我们又是反向存储,所以需要在currentresult头部加i个"0", i=pop次数
1.6
num2所有数都用过了即num2="",结束循环
1.7
考虑一些收尾工作,进位有没有多, 要不要连续进位
1.8 将累加结果result反转,
得到真实结果
可以看到暴力法虽然思路直接,但是需要处理的东西也不少,而且时间空间效率都不理想,
权当一个编码锻炼.
暴力代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 
 | class Solution {public:
 string multiply(string num1, string num2)
 {
 if (num1 == "0" || num2 == "0")
 return "0";
 
 string result;
 string tempresult;
 string zerostring;
 int i;
 int cflag = 0;
 while (num2 != "")
 {
 for (i = num1.size() - 1; i = 0; i--)
 {
 tempresult.push_back('0' + ((num1[i] - '0')*(num2.back() - '0') + cflag) % 10);
 cflag = ((num1[i] - '0')*(num2.back() - '0') + cflag) / 10;
 }
 
 if (cflag  0)
 tempresult.push_back('0' + cflag);
 
 tempresult = zerostring + tempresult;
 
 ADDstring(result, tempresult);
 
 num2.pop_back();
 
 tempresult.clear();
 zerostring += "0";
 cflag = 0;
 }
 
 
 
 string reverseresult;
 for (int j = result.size() - 1; j = 0; j--)
 reverseresult.push_back(result[j]);
 return reverseresult;
 }
 
 void ADDstring(string& up, string& down)
 {
 int j = 0;
 int cflag = 0;
 int temp = 0;
 
 while (j < down.size())
 {
 if (j < up.size())
 {
 temp = (up[j] - '0') + (down[j] - '0') + cflag;
 up[j] = (temp) % 10 + '0';
 cflag = (temp) / 10;
 }
 else
 {
 up.push_back(((down[j] - '0') + cflag) % 10 + '0');
 cflag = ((down[j] - '0') + cflag) / 10;
 }
 
 j++;
 }
 while (cflag != 0)
 {
 if (j < up.size())
 {
 temp = (up[j] - '0') + cflag;
 up[j] = (temp) % 10 + '0';
 cflag = (temp) / 10;
 }
 else
 {
 up.push_back(cflag % 10 + '0');
 cflag = cflag / 10;
 }
 }
 
 
 }
 
 };
 
 | 
2.
优化思路——每一位数的乘法运算对result影响分析
//1 2 3 num1[i] //为示范简单,这里序号从右往左编码,程序中需要转换 //4
5 6 num2[j] //例如num1[0]和num2[0]相乘 : 3X6=18
肯定影响到了result[0+0],有进位则会影响到result[0+0+1],
result其他的值则肯定不会改变
2.1
优化分析: 在竖式中,从右向左编码, 两个数num1[i], num2[j]相乘,
则只会影响到result[i+j] , result[i+j+1]两位的结果
2.2
因此我们不需要临时存储的容器currentresult, 只需要在
result中修改受影响的两位即可
2.3
另外num2.pop_back虽然符合直觉习惯,但显然我们可以通过移动下标来代替反复的pop
事实证明对于一些修改字符串的操作还是小心为慎,
虽然pop这个操作看起来只需要剪掉尾巴就好, 应该耗时影响不大 ,
但事实上我卡在80%就是因为它!!!!, 优化掉后直接97%=
=.
2.4
反转问题: 同上可知, 乘法最远也就影响到result[length1+length2+1].
即result长度已知, 因此可以反向填充result即可避免最后还得反转
暴力法时因为不清楚result到底会有多长, 所以不能反向填充.
2.5 另外还有一些小细节思考
比如连续进位有没有问题
循环结束需不需要有收尾整理
我们现在result里填充了[length1+length2+1]的
'0' 方便我们后续反向填充和累加, 但是result[0]是最后一位进位的结果,
假如最后没有进位呢?
优化代码, 超越97%时间,86%内存
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 
 | class Solution {public:
 string multiply(string num1, string num2)
 {
 if (num1 == "0" || num2 == "0")
 return "0";
 
 
 string result;
 int totallength=num1.size()+num2.size();
 int i;
 int j=0;
 int cflag =0;
 int temp=0;
 
 
 for(i=0;i<totallength;i++)
 {
 result.push_back('0');
 }
 
 
 while (j<num2.size())
 {
 for (i = num1.size() - 1; i = 0; i--)
 {
 temp=(result[totallength-1-((num1.size()-i-1)+j)]-'0')+ (num1[i] - '0')*(num2[num2.size()-1-j] - '0');
 
 
 result[totallength-1-((num1.size()-i-1)+j+1)]+=temp/10;
 result[totallength-1-((num1.size()-i-1)+j)]='0'+temp%10;
 }
 
 
 j++;
 }
 
 
 
 
 
 
 
 if(result[0]!='0')
 return result;
 else
 return result.substr(1);
 }
 
 
 };
 
 | 
string.pop_back()真的卡了我好久, 左优化右优化, 去头部0的方法换了又换,
就是卡在80% .......谨慎考虑库函数的效率.