Given two numbers represented as strings, return multiplication of the numbers as a string.
Note: The numbers can be arbitrarily large and are non-negative.
第一眼看到这个题目,潜意识里觉得直接将字符串转换为数字相乘,然后将结果再转换为字符串,难道这题考的是字符串与数值之间的转换?
细看,发现数字可能非常大,那么问题来了:这个数据类型怎么定义呢?显然这种思路完全是错的了。
那么怎么解决这个问题呢?仔细一想问题可能在于如何理解这个乘法过程(题目中multiply可是关键字)了。由于经验尚浅,花了一些时间去琢磨,最后参照一些资料做出了如下分析:(算法题还是有很多值得用心思考的地方的,要学会抓住解决问题的痛点,和把握细节)
1、要模拟竖式乘法的计算过程(透过问题看本质)
2、这个过程有乘法思维,还有加法思维在里面(因为涉及到进位)
3、要把乘法和加法操作过程分清楚,每次一个新位与被乘数相乘之前,都要加上进位再处理。(注意细节)
4、为了顺应习惯,把两个数字reverse过来再操作,最后还原顺序。
代码如下:
class Solution { public: string multiply(string num1, string num2) { int m = num1.size(), n = num2.size();//字符串元素个数 if (num1 == "0" || num2 == "0") return "0"; vector<int> res(m+n, 0); reverse(begin(num1),end(num1));//字符串反转 reverse(begin(num2),end(num2)); for (int i = 0; i < n; ++i) for (int idx = i, j = 0; j < m; ++j) res[idx++] += (num2[i]-‘0‘)*(num1[j]-‘0‘);// 字符转化为数字然后相乘(ascll码) int carry = 0; for (int i = 0; i < m+n; ++i) { int tmp = res[i]; res[i] = (tmp+carry)%10; carry = (tmp+carry)/10;//进位 } string str(m+n,‘0‘); for (int k = 0, i = res.size()-1; i >= 0; --i) str[k++] = res[i]+‘0‘;//还原顺序 auto idx = str.find_first_not_of(‘0‘);//将str字符串中第一个不匹配字符‘0’的索引值赋给idx return str.substr(idx);//从起始字符序号idx开始取得str中的子字符串(消除了前面的0) } };
其他解法:
1、class Solution { public: string multiply(string num1, string num2) { string sum(num1.size() + num2.size(), ‘0‘); for (int i = num1.size() - 1; 0 <= i; --i) { int carry = 0; for (int j = num2.size() - 1; 0 <= j; --j) { int tmp = (sum[i + j + 1] - ‘0‘) + (num1[i] - ‘0‘) * (num2[j] - ‘0‘) + carry; sum[i + j + 1] = tmp % 10 + ‘0‘; carry = tmp / 10; } sum[i] += carry; } size_t startpos = sum.find_first_not_of("0"); if (string::npos != startpos) { return sum.substr(startpos); } return "0"; } };
This is an example of the pretty straightforward but very efficient C++ solution with 8ms runtime on OJ. It seems most of people here implemented solutions with base10 arithmetic, however that is suboptimal. We should use a different base.
This was a hint. Now stop, think, and consider coding your own solution before reading the spoiler below.
The idea used in the algorithm below is to interpret number as number written in base 1 000 000 000 as we decode it from string. Why 10^9? It is max 10^n number which fits into 32-bit integer. Then we apply the same logic as we used to hate in school math classes, but on digits which range from 0 to 10^9-1.
You can compare the multiplication logic in other posted base10 and this base1e9 solutions and you‘ll see that they follow exactly same pattern.
Note, that we have to use 64-bit multiplication here and the carry has to be a 32-bit value as well
class Solution { public: void toBase1e9(const string& str, vector<uint32_t>& out) { int i = str.size() - 1; uint32_t v = 0; uint32_t f = 1; do { int n = str[i] - ‘0‘; v = v + n * f; if (f == 100000000) { out.push_back(v); v = 0; f = 1; } else { f *= 10; } i--; } while (i >= 0); if (f != 1) { out.push_back(v); } } string fromBase1e9(const vector<uint32_t>& num) { stringstream s; for (int i = num.size() - 1; i >= 0; i--) { s << num[i]; s << setw(9) << setfill(‘0‘); } return s.str(); } string multiply(string num1, string num2) { if (num1.size() == 0 || num2.size() == 0) return "0"; vector<uint32_t> d1; toBase1e9(num1, d1); vector<uint32_t> d2; toBase1e9(num2, d2); vector<uint32_t> result; for (int j = 0; j < d2.size(); j++) { uint32_t n2 = d2[j]; int p = j; uint32_t c = 0; for (int i = 0; i < d1.size(); i++) { if (result.size() <= p) result.push_back(0); uint32_t n1 = d1[i]; uint64_t r = n2; r *= n1; r += result[p]; r += c; result[p] = r % 1000000000; c = r / 1000000000; p++; } if (c) { if (result.size() <= p) result.push_back(0); result[p] = c; } } return fromBase1e9(result); } };