LeetCode 43. 字符串相乘(熟悉而又陌生的大数相乘)

2020年7月15日 周三 天气晴 【不悲叹过去,不荒废现在,不惧怕未来】



题目简介

LeetCode 43. 字符串相乘
在这里插入图片描述
记得我第一次接触大数相乘的时候,还是在大二刚进实验室的时候,师兄两天给我们培训完C语言后布置了几道题,里面就有这个大数相乘,当时可以说啥也不会啊,没想到终究还是绕不过这道题hhh~这里介绍两种方法,方法一比较好想,方法二需要点技巧。


解法1. 将所有数据不进位直接存入数组,最后统一进位(记住这种即可)

  1. 把两个数用数组 a, b 来存储,并且反转(从个位开始乘)。
  2. 对于 a 的第 i 位 和 b 的第 j 位相乘的结果存储在 c[i + j] 上,即 c[i + j] += a[i] * b[j]。这里用加号是因为有多种情况都会映射到 i + j 位上。
  3. 最后,从 c 的低位向高位整理,c[i + 1] = c[i] / 10, c[i] %= 10。

举个栗子:
在这里插入图片描述
代码:

class Solution {
public:
    string multiply(string num1, string num2) {
        int len1 = num1.size(), len2 = num2.size();
        // 不进位直接存放在数组中
        vector<int> num(len1+len2);
        for(int i=len1-1;i>=0;--i){
            for(int j=len2-1;j>=0;--j){
                int n1 = num1[i] - '0', n2 = num2[j] - '0';
                num[i+j+1] += n1*n2;
            }
        }

        // 开始进位
        string ans;
        int c = 0, r = 0;
        for(int i=len1+len2-1;i>=0;--i){
            r = (num[i]+c)%10;
            c = (num[i]+c)/10;
            ans = to_string(r) + ans;
        }

        // 去除前导0
        while(ans.size()>1 && ans[0]=='0') ans.erase(ans.begin());

        return ans;
    }
};

解法2. 竖式乘法(代码简洁,但是细节不好理解)

首先,长度为 m 的数乘以长度为 n 的数的结果不超过 m+n。

接下来看一下两数相乘的计算过程,从右向左,将数2中的每一位的数与数1相乘,最后将结果相加。下图演示的是两数相乘的整个过程,从下图中,我们可以得到,对于num[i] *num[j],数1中的第 i 位数字与数2中的第 j 位数字相乘的结果只会存放在第【i+j】和【i+j+1】这两位上。然后再竖着相加,就能得到结果了。
在这里插入图片描述
核心思想: 每次只更新低位(相对于数字而言)【i+j+1】上的数,而不管高位【i+j】上的数是否超过10(即使超过了10 ,字符也能存储,并且后面都会对超过10的位置进行数字更新,同时首位字符代表的数字一定不会超过10,不然就违反了 ”长度为 m 的数乘以长度为 n 的数的结果不超过 m+n” 这句话)。

class Solution {
public:
    string multiply(string num1, string num2) {
        int len1 = num1.size(), len2 = num2.size();
        string ans(len1+len2,'0');
        for(int i=len1-1;i>=0;--i){
            for(int j=len2-1;j>=0;--j){
                /* 1. 这里只加上低位 (ans[i+j+1]-'0'),是因为高位可以暂时不管,即使超过了10 ,
                字符也能存储(计算字符和 '0' 的偏移量就是数字的值)*/
                int tmp = (ans[i+j+1]-'0') + (num1[i]-'0')*(num2[j]-'0');
                ans[i+j+1] = tmp%10 + '0';
                /* 2. 这里之所以没考虑进位,是因为第 1 点 */
                /* 3. 另外,首位不用担心相加会超过10,因为之前说了,
                长度为 m 的数乘以长度为 n 的数的结果不超过 m+n,所以首位一定不会进位,否则就超过 m+n 了 */
                ans[i+j] += tmp/10;
            }
        }
        while(ans.size()>1 && ans[0]=='0') ans.erase(ans.begin());
        return ans;
    }
};

刚看完代码,感觉 res[i+j] += temp/10; 这一步好像没有进位,不过仔细想想会发现,这一步即使相加超过了 10 也不用进位,因为后面再次执行这一步 int tmp = (ans[i+j+1]-'0') + (num1[i]-'0')*(num2[j]-'0'); 的时候会把没有进的位加上去。另外首位不用担心相加会超过10,因为之前说了,长度为 m 的数乘以长度为 n 的数的结果不超过 m+n,所以首位一定不会进位,否则就超过 m+n 了。


参考文献

https://leetcode-cn.com/problems/multiply-strings/solution/si-lu-qing-xi-by-lllllliuji-2/
https://leetcode-cn.com/problems/multiply-strings/solution/c-shu-shi-cheng-fa-dai-ma-jian-ji-you-ya-yi-dong-b/
https://leetcode.com/problems/multiply-strings/discuss/17605/Easiest-JAVA-Solution-with-Graph-Explanation
https://blog.csdn.net/kangkanglou/article/details/79894208

十分感谢以上这些大神们。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值