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

  • Post author:
  • Post category:其他



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

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



版权声明:本文为m0_37433111原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。