1、思路
(字符串模拟) O(n∗m)
普通竖式
以num1 = 123 , num2 = 456为例:我们遍历 num2 每一位与 num1 进行相乘,将每一步的结果进行累加,在这个过程如果相乘或者相加的结果大于等于10 ,我们都要去满10进位,如下图所示:
这样模拟普通竖式计算的方法较为复杂,我们可以考虑优化版的竖式计算。
优化竖式
其实在相乘或者相加计算过程的每一位,我们可以考虑先不去满10进位,等到计算完所有的相乘结果以后,最终将其加到一块,再去满10进位 ,最后的结果和普通竖式 一样,但却可以大大简化我们的模拟过程。(如下图所示)
具体过程如下:
1、长度是n和长度是m的数字相乘,最多只有n + m位,为了方便计算,将num1和num2反向存储到A[]和B[]中,即位数低的在数组前面,且开一个大小是n + m的C[]存储计算后的答案。
2、两个数相乘时,将A[i] * B[j]的结果累加到C[i + j]中,最后C[i + j]表示i + j这个位数的值是C[i + j](如上图所示)
3、由于C[]数组中的某些位数字可能是大于等于10的,我们从0枚举到n + m - 1,进行满10进位, 将所有位的值全部变成个位数。
4、最后将C[]数组反转输出。
细节:
最终得到的数组C[]的高位可能包含前导0,因此在反转之前要先去除高位前导0。
时间复杂度分析: O(n∗m),n和m分别是num1和num2的长度。
class Solution {
public:
string multiply(string num1, string num2) {
vector<int> A, B;
int n = num1.size(), m = num2.size();
for (int i = n - 1; i >= 0; i -- ) A.push_back(num1[i] - '0'); //反向存贮
for (int i = m - 1; i >= 0; i -- ) B.push_back(num2[i] - '0');
vector<int> C(n + m);
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
C[i + j] += A[i] * B[j];
int t = 0; //存贮进位
for (int i = 0; i < C.size(); i ++ ) {
t += C[i];
C[i] = t % 10;
t /= 10;
}
int k = C.size() - 1;
while (k > 0 && !C[k]) k -- ; //去除前导0
string res;
while (k >= 0) res += C[k -- ] + '0'; //反转
return res;
}
};
///
class Solution {
public:
string multiply(string num1, string num2) {
if (num1 == "0" || num2 == "0")
return "0";
// 反转字符串数组,存入整形数组里
vector<int> v1(num1.size()), v2(num2.size());
vector<int> v3(num1.size() + num2.size());
string s;
for (int i = 0; i < num1.size(); i++) {
v1[i] = num1[i] - '0';
}
for (int i = 0; i < num2.size(); i++) {
v2[i] = num2[i] - '0';
}
reverse(v1.begin(), v1.end());
reverse(v2.begin(), v2.end());
/*for (int i = 0; i < 3; i++)
{
cout << v1[i] << " " << v2[i] << endl;
}*/
for (int i = 0; i < num1.size(); i++) {
for (int j = 0; j < num2.size(); j++) {
v3[i + j] += v1[i] * v2[j];
}
}
int next = 0; // 存进位
for (int i = 0; i < v3.size(); i++) {
next += v3[i];
v3[i] = next % 10;
next /= 10;
}
for (int i = 0; i < v3.size(); i++) {
s += to_string(v3[i]);
}
reverse(s.begin(), s.end());
int k = 0;
while (1) {
if (s[k] == '0')
k++;
else
break;
}
s.erase(0, k);
return s;
}
};
总结:字符串相乘
1)用了三种方法,都是不够优化,全都太暴力爆数据。1.用暴力求出每一位数字跟num2的每一位相乘的和,当数字相加的时候,数据就爆了,存不下。
2)那只能往字符串方面考虑,可是字符串不容易实现每一位的相乘和相加运算。这个时候就要仔细研究竖式相乘的方法和步骤,每次把相乘的结果写下来,不进位,那么能得到的都是一个既能大于10,又能小于10的数字,,让他们分别存入数组,可以用这些小数组进行运算,
3)为了方便运算,将num1 num2数组的数据逆存在数组v1 v2里,然后对每一位相乘的结果进行相加,存入v3里,但是这里要注意,不是带10的次方进取相乘,不然存不下,只带小数进行相乘。就是v3[i+j]+=v1[i]*v2[j] 就可以满足每次都是将竖式计算的同一列结果加入到v3中
4)1、长度是n和长度是m的数字相乘,最多只有n + m位,为了方便计算,将num1和num2反向存储到A[]和B[]中,即位数低的在数组前面,且开一个大小是n + m的C[]存储计算后的答案。
2、两个数相乘时,将A[i] * B[j]的结果累加到C[i + j]中,最后C[i + j]表示i + j这个位数的值是C[i + j](如上图所示)
3、由于C[]数组中的某些位数字可能是大于等于10的,我们从0枚举到n + m - 1,进行满10进位, 将所有位的值全部变成个位数。
4、最后将C[]数组反转输出。
细节:
最终得到的数组C[]的高位可能包含前导0,因此在反转之前要先去除高位前导0。
时间复杂度分析: O(n∗m),n和m分别是num1和num2的长度。