43. Multiply Strings [Medium]

直接将String转为数字相乘会超过int和long的范围,因此需要拆解乘法过程来得到答案

一、时间复杂度改良

本题什么时候进位有两种处理,后者较好,能降低算法时间复杂度

1. 每两个数字相乘都在结果中进位

优点:最后得到的res矩阵能直接代表计算结果,从前往后遍历并append就能得到所需字符串 

缺点:每次计算乘积后都要进位,即进位操作在双层循环中,大幅度增加时间复杂度

2. 每次计算保留每两个数字相乘的原始结果,最后进位

优点:统一处理进位,在单层循环中,降低算法时间复杂度

缺点:最后必须从后往前处理res矩阵来进位,构造字符串需要用到insert方法来使后面遍历到的数字插在头部

二、空间复杂度改良

本题用可以用矩阵or数组存放乘法拆解的中间计算结果

1. 矩阵

优点:更直观、容易想到,每两个数字相乘的结果放在矩阵的一个格子中

缺点:空间消耗大,且之后处理矩阵中的结果又需要一个双层循环,不仅增大空间复杂度,还增大了时间复杂度

2. 数组

优点:二维降一维,降低空间复杂度,且乘法拆解计算的过程中就完成了每一列计算结果的累加

缺点:思维上复杂一点,要想到可以累加每一步的计算结果

/**
 * 自己不会做
 * 该思路拆解除法过程,得到两个数字每一位与每一位相乘的结果,保存在矩阵中
 * 得到矩阵再最后统一进位、表示为字符串,这样能省去每一步都要进位或要字符串相加的麻烦
 * Runtime: 3 ms, faster than 90.66%
 * Memory Usage: 39.1 MB, less than 45.44%
 */
class Solution {
    public String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        char[] arr1 = num1.toCharArray(), arr2 = num2.toCharArray();
        int row = arr2.length, col = arr1.length + arr2.length - 1; 
        int[][] res_matrix = new int[row][col]; // matrix to store intermediate results during multiplication
        for (int i = 0; i < arr1.length; i++) { // calculate the 1st row to avoid make accumulation simpler afterwards
            res_matrix[0][i] = (arr1[i] - '0') * (arr2[0] - '0');
        }
        for (int i = 1; i < arr2.length; i++) { // calculate and accumulate the results in the same col
            for (int k = 0; k < i; k++) {
                res_matrix[i][k] = res_matrix[i - 1][k];
            }
            for (int j = 0; j < arr1.length; j++) {
                res_matrix[i][i + j] = (arr1[j] - '0') * (arr2[i] - '0') + res_matrix[i - 1][i + j];
            }
        }
        StringBuilder res = new StringBuilder();
        for (int i = col - 1; i > 0; i--) { // carry bit at once
            res.insert(0, res_matrix[row - 1][i] % 10); // lengthen res from the head
            res_matrix[row - 1][i - 1] += res_matrix[row - 1][i] / 10;
        }
        res.insert(0, res_matrix[row - 1][0]);
        return res.toString();
    }
}
/**
 * 和上面同样的做法,但写法改良了
 * 可以只用一维的结果数组,每次更新都直接累加现有的计算结果
 */
class Solution {
    public String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        char[] arr1 = num1.toCharArray(), arr2 = num2.toCharArray();
        int[] res = new int[arr1.length + arr2.length - 1]; // array to keep intermediate results during multiplication process
        for (int i = 0; i < arr1.length; i++) {
            for (int j = 0; j < arr2.length; j++) {
                res[i + j] += (arr1[i] - '0') * (arr2[j] - '0'); // accumulate
            }
        }
        StringBuilder sb = new StringBuilder();
        for (int i = res.length - 1; i > 0; i--) {
            sb.insert(0, res[i] % 10);
            res[i - 1] += res[i] / 10;
        }
        sb.insert(0, res[0]);
        return sb.toString();
    }
}
/**
 * 仍然和前两个同样的思路,但是每次计算出结果都进位
 * 同样能得到正确答案但是时间复杂度大,运行时间也长了
 */
class Solution {
    public String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        char[] arr1 = num1.toCharArray(), arr2 = num2.toCharArray();
        int[] res = new int[arr1.length + arr2.length - 1];
        for (int i = arr1.length - 1; i >= 0; i--) {
            for (int j = arr2.length - 1; j >= 0; j--) {
                res[i + j] += (arr1[i] - '0') * (arr2[j] - '0');
                if (i != 0 || j != 0) {
                    res[i + j - 1] += res[i + j] / 10;
                    res[i + j] %= 10;
                }
            }
        }
        StringBuilder sb = new StringBuilder();
        for (int i : res) {
            sb.append(i);
        }
        return sb.toString();
    }
}
/**
 * 思路稍微有变化,把一对单个数字的乘积看作两位数,从后往前计算并持续进位
 * 十位和个位分别改变res数组的 i+j 和 i+j+1 两个位置
 * 这个方法在最后需要先判断第一位是否为0,为0不能append
 * Runtime: 7 ms, faster than 39.55%
 * Memory Usage: 39.2 MB, less than 46.63%
 */
class Solution {
    public String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        char[] arr1 = num1.toCharArray(), arr2 = num2.toCharArray();
        int[] res = new int[arr1.length + arr2.length]; // take every multiplication of two num as 2 digits
        for (int i = arr1.length - 1; i >= 0; i--) {
            for (int j = arr2.length - 1; j >= 0; j--) {
                int mul = (arr1[i] - '0') * (arr2[j] - '0');
                int p1 = i + j, p2 = i + j + 1, temp = mul + res[p2];
                res[p2] = temp % 10; // carry digits
                res[p1] += temp / 10; // carry digits
            }
        }
        StringBuilder sb = new StringBuilder();
        if (res[0] != 0) {
            sb.append(res[0]);
        }
        for (int i = 1; i < res.length; i++) {
            sb.append(res[i]);
        }
        return sb.toString();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值