本次做的是大数相乘的题目。
1. 题目
Given two non-negative integers num1
and num2
represented as strings, return the product of num1
and num2
.
Note:
- The length of both
num1
andnum2
is < 110. - Both
num1
andnum2
contains only digits0-9
. - Both
num1
andnum2
does not contain any leading zero. - You must not use any built-in BigInteger library or convert the inputs to integer directly.
- The length of both
给出两个非负的数字num1和num2,用字符串string来表示,返回num1和num2相乘的结果。
注意:
- num1和num2的长度小于110;
- num1和num2都是只包含0-9的数字;
- num1和num2度的高位不是0;
- 不能使用已有的BigInteger 库或者直接将输入转化为整数。
2.思路
题目很容易理解,就是字符串的相乘,最简单的思路,就是按照手算时的
竖式乘法
来计算:较长的数字num1在上,较短的数字 num2在下,举个例子。num1乘以num2的每一位,得到的结果存储起来,再进行相加。按照这种思路,存储结果可以用字符串也可以用数组。我是采用数组的形式。数组r初始化为”num2.length x(num1.length()+num2.length())”的大小,且全为0.因为num2的长度决定了num1要乘几次,如下面例子,num1要乘4次,得到四行结果。最后相加的时候,结果的位数最多是num1.length() + num2.length()这么长。因此,得到这个全0矩阵后,每一列相加,就相当于竖式的加法计算。
1 2 3 4 5
x 2 2 2 4
————————————
0 | 0 | 0 | 0 | 4 | 9 | 3 | 8 | 0 |
0 | 0 | 0 | 2 | 4 | 6 | 9 | 0 | 0 |
0 | 0 | 2 | 4 | 6 | 9 | 0 | 0 | 0 |
0 | 2 | 4 | 6 | 9 | 0 | 0 | 0 | 0 |
————————————
0 2 7 4 5 5 2 8 0
按照上面例子的算法,可以得到计算结果。在最高位时,判断如果是0,则舍弃该位,若是有进位,则需要保存。
注意的是,当num1或者num2其中一个是0时,不需要计算,直接返回字符串“0”;还有,num1始终是较长的字符串,num2是较短的字符串。
3.代码
string multiply(string num1, string num2) {
//如果两个字符串有一个为0,那么相乘结果直接为0
if (num1 == "0" || num2 == "0")
return "0";
//比较两个字符串的长度,长的作为num1
if (num1.length() < num2.length())
{
string s;
s = num1;
num1 = num2;
num2 = s;
}
int i,j;
int k=0;
int len = num1.length() + num2.length();
//定义一个初始化为全0的矩阵来存放num1乘以num2每一位的计算结果,矩阵行数是num2的长度,矩阵列数是num1长度+num2长度
int r[num2.length()][len];
for(i = 0; i < num2.length(); i ++)
for(j = 0; j < len; j ++)
r[i][j] = 0;
//c用于记录进位
int c = 0;
//num2的最后一位开始,num1也是从最后一位开始,依次按位相乘
for(i = num2.length()-1; i >= 0; i --){
for(j = num1.length() - 1; j >=0 ; j --){
int temp = (num1[j]-'0') * (num2[i]-'0') + c;
c = temp / 10;
//num1乘以num2的第i位存放在第k行,k从0开始
r[k][i+j+1] = temp % 10;;
}
r[k][i+j+1] = c;
k ++;
c = 0;
}
//sum用于记录矩阵每一列数字加起来的结果,c记录进位
int sum = 0;
c = 0;
string res = "";
for(int col = len-1; col >= 0; col --){
for(int row = 0; row < num2.length(); row ++){
sum += r[row][col];
}
sum += c;
c= sum / 10;
//如果最高位这一列结果是0,则不需要加到res中
if((col == 0) && (sum == 0))
break;
res += (sum%10)+'0';
sum = 0;
}
//翻转字符串
reverse(res.begin(), res.end());
return res;
}
4.优化的方法
字符串很长的时候,必然会有很多重复的数字,那么此时可以进行优化。定义一个int数组,记为h,大小为10,分别表示num2中数字0-9出现过没有,出现过,则将出现在第几位记录在数组h中。以上面例子来讲,当num1乘以num2中的4时,记录h[4] = k, 表示在矩阵的第k行;当num1乘以num2中的第一个2时,记录h[2] ;那么当再次出num2中再出现2时,就可以直接从矩阵的r[h[2]]行取结果,放在当前行,并前移几个单位即可。
我认为这种做法在字符串长时会有一定的改进。代码添加修改部分如下:
int h[10];
for(i = 0; i < 10; i ++)
h[i] = 0;
//c用于记录进位
int c = 0;
//num2的最后一位开始,num1也是从最后一位开始,依次按位相乘
for(i = num2.length()-1; i >= 0; i --){
if (h[(num2[i]-'0')] != 0)
{
int d = k - h[(num2[i]-'0')];
for(int l = len - 1; l >= d;l--)
r[k][l-d] = r[h[(num2[i]-'0')]][l];
}
else{
for(j = num1.length() - 1; j >=0 ; j --){
int temp = (num1[j]-'0') * (num2[i]-'0') + c;
c = temp / 10;
//num1乘以num2的第i位存放在第k行,k从0开始
r[k][i+j+1] = temp % 10;;
}
h[(num2[i]-'0')] = k;
r[k][i+j+1] = c;
}
k ++;
c = 0;
}