字符串相乘
本篇文章将对leetcode的字符串相乘这一道题目进行讲解,题目链接如下:
43.字符串相乘
题目
给定两个以字符串形式的非负整数num1
和num2
,返回num1
和num2
的乘积,它们的乘积也表示为字符串形式
**注意:**不能使用内置的BigInteger库或直接将输入转换位整数。
提示:
num1
和num2
都只能由数字组成且不包含前导01 <= num1.length, num2.length <= 200
class Solution{
public:
string multiply(string num1, string num2){
}
}
分析
- 首先,转换成int再进行相乘一定是不行的,题目所给的数据范围说明了这个数有可能是一个很大的数,大到超过long long 的数据范围
- 所以要解这道题,我们只能是模拟我们做乘法的过程,这题也可以算是两个大数相乘的模板题。
思路
回忆一下小学我们是如何做两个数的乘法的(doge),让第一个数和第二个数的每一个数相乘,然后再不断的进位相加即可。
那么要编程实现这一过程,为了思路清晰,我们可以分模块实现
string mul(string& num1, int x
实现第一个数和一个小数相乘(第二个数的某一位)
void add(string& ans, string tmp, int k)
实现两个大数的进位相乘,其中k表示要进的位数,ans存储相加后的值。
重点
在实现两个大数相加的过程时,如果我们按照正常的思路从低位加到高位的话,很容易会发现想要确定最终答案string的长度是一件很麻烦的事,所以我们不难想到如果存在string内的数是相反的顺序,那么就对于string长度的确定问题就简单了很多,变成了一个较为易解的双指针问题,所以在mul
函数返回的string应该是与答案相反的便于供add()
使用。
如图:
实现
首先,先不写上面两个函数的实现逻辑,先把接口函数的大框架先搭出来,也就是整个模拟乘法的过程如下:
string multiply(string num1, string num2) {
string ans;
int now = 1,k=0;
//~i等价于i>=0
//这是由于当i==-1时根据整型在内存中是补码的存储格式
//-1在内存中的二进制格式为全一,取反就是0,表示结束循环
for(int i = num2.size()-1; ~i; --i)
//在答案string上以此进位加上num1和num2的每位数的相乘
//k表示当前是进位到了第几位
//每次相加之后,进位要增加,所以要k++
//这里由于num2中存的时字符,所以化作整型时应该先减去字符'0'
add(ans,mul(num1,num2[i]-'0'),k++);
int end = ans.size()-1;
//由于在存储中采用了倒序存储,所以有可能存在前导0
//在输出答案时应该要先把前导0删去
//但是要注意答案就是0的情况需要保留一个0
while(end&&ans[end--]=='0') ans.pop_back();
//逆置string得到答案
reverse(ans.begin(),ans.end());
return ans;
}
string mul(string& num1, int x
函数
string mul(string& num1, int x)
{
//如果乘数存在0,直接返回0即可
if(x == 0) return "0";
string tmp;
//end1指向num1的最后一个数字,开始模拟乘法
//top表示进位
int top = 0, end1 = num1.size()-1;
//与上面同理,表示end1>=0
while(~end1)
{
//同样有字符和数字的转换问题
int t = (num1[end1]-'0')*x+top;
top = t/10;
//利用尾插来达到将最终答案逆置的效果
//记住答案也需要加上字符'0'
tmp.push_back(char(t%10+'0'));
--end1;
}
//如果最后进位不是0,仍然需要加上top
if(top) return tmp + char(top+'0');
else return tmp;
}
void add(string& ans, string tmp, int k)
函数
//由于mul的string是相反的答案
//所以add时只需要从前往后遍历即可
void add(string& ans, string tmp, int k)
{
//由于是进位加法,所以从ans的第k位才开始加
int end1 = k, end2 = 0;
//top表示进位
int len1 = ans.size(), len2 = tmp.size();
int top = 0;
while(end1<len1||end2<len2)
{
//先用t来保存该位数以及top相加之后的值
int t = top;
if(end1 < len1) t+=ans[end1] - '0';
if(end2 < len2) t+=tmp[end2] - '0';
top = t/10;
//如果这时end1还没有到达最后,那么直接修改原数据即可
if(end1< len1) ans[end1] = t%10+'0';
//如果已经到达最后了,说明需要进行尾插
else ans.push_back(char(t%10+'0'));
++end1;++end2;
}
//一定要记得判断是否需要进位!!!
if(top) ans.push_back('1');
}
整体答案
class Solution {
public:
string mul(string& num1, int x)
{
if(x == 0) return "0";
string tmp;
int top = 0, end1 = num1.size()-1;
while(~end1)
{
int t = (num1[end1]-'0')*x+top;
top = t/10;
tmp.push_back(char(t%10+'0'));
--end1;
}
if(top) return tmp + char(top+'0');
else return tmp;
}
void add(string& ans, string tmp, int k)
{
int end1 = k, end2 = 0;
int len1 = ans.size(), len2 = tmp.size();
int top = 0;
while(end1<len1||end2<len2)
{
int t = top;
if(end1 < len1) t+=ans[end1] - '0';
if(end2 < len2) t+=tmp[end2] - '0';
top = t/10;
if(end1< len1) ans[end1] = t%10+'0';
else ans.push_back(char(t%10+'0'));
++end1;++end2;
}
if(top) ans.push_back('1');
}
string multiply(string num1, string num2) {
string ans;
int now = 1,k=0;
for(int i = num2.size()-1; ~i; --i)
{
//cout << mul(num1,num2[i]-'0')<<endl;
add(ans,mul(num1,num2[i]-'0'),k++);
}
int end = ans.size()-1;
//cout << end << endl;
while(end&&ans[end--]=='0') ans.pop_back();
reverse(ans.begin(),ans.end());
return ans;
}
};