从零刷算法-字符串相乘

137 阅读1分钟

「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战」。

题目描述

给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。

示例 1:

输入: num1 = "2", num2 = "3"
输出: "6"

示例 2:

输入: num1 = "123", num2 = "456"
输出: "56088"

提示:

  • 1 <= num1.length, num2.length <= 200
  • num1 和 num2 只能由数字组成。
  • num1 和 num2 都不包含任何前导零,除了数字0本身。

题目链接:[字符串相乘]

思路介绍

  • 首先最直接的思路就是小学学的竖式解决乘法问题。首先将一个数和另外一个数的每一项相乘,然后得到的数进行错位相加。
  • 优化版本的竖式是首先要知道一个规律,首先两个长度为m和n的数相乘,最终结果不会超过m+n。因此最终数组申请长度就为m+n。然后就是两个数的第i位和第j位相乘的结果,会存放在最终结果的第i+j位和第i+j+1位中。
  • 在优化的代码运行中,mul[i+j]可能会出现大于10的数,但是因为遍历是从低位到高位相乘的,在填充mul数组时首先保证了低位是个位数,至于高位,在后序遍历中会作为低位保证其为个位数。所以代码可以执行。因为首先保证结果不会大于m+n,所以数组够用。第一位不会大于等于10.

具体过程如下:

  • 长度是n和长度是m的数字相乘,最多只有n + m位,为了方便计算,将num1和num2反向存储到A[]和B[]中,即位数低的在数组前面,且开一个大小是n + m的C[]存储计算后的答案。
  • 两个数相乘时,将A[i] * B[j]的结果累加到C[i + j]中,最后C[i + j]表示i + j这个位数的值是Ci + j
  • 由于C[]数组中的某些位数字可能是大于等于10的,我们从0枚举到n + m - 1,进行满10进位, 将所有位的值全部变成个位数。
  • 最后将C[]数组反转输出。

注意:最终得到的数组C[]的高位可能包含前导0,因此在反转之前要先去除高位前导0

代码

class Solution {
    public String multiply(String num1, String num2) {
​
        int n = num1.length(), m = num2.length();
        int[] A = new int[n], B = new int[m];
        for (int i = n - 1; i >= 0; i--) A[n - 1 - i] = num1.charAt(i) - '0'; //反向存贮
        for (int i = m - 1; i >= 0; i--) B[m - 1 - i] = num2.charAt(i) - '0';
​
        int[] C = new int[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.length; i++) {
            t += C[i];
            C[i] = t % 10;
            t /= 10;
        }
        int k = C.length - 1;
        while (k > 0 && C[k] == 0) k--;   //去除前导0
        StringBuilder sb = new StringBuilder();
        while (k >= 0) sb.append((char)(C[k--] + '0')); //反转
        return sb.toString();
    }
}

运行结果

执行结果:通过

执行用时:2 ms

内存消耗:41.8 MB