Leetcode 43. 字符串相乘

题目

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

示例 1:

输入: num1 = “2”, num2 = “3”
输出: “6”

示例 2:

输入: num1 = “123”, num2 = “456”
输出: “56088”

说明:

  • num1 和 num2 的长度小于110。
  • num1 和 num2 只包含数字 0-9。
  • num1 和 num2 均不以零开头,除非是数字 0 本身。
  • 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。

解答

解法一:平行乘法

这是最直观的解法,最符合我们平时计算乘法的方式。

分为以下几步:

  1. 从低到高位计算出每一行的乘积字符串。
  2. 将计算出的每一行乘积字符串补上合适个数的 0 ,并保存起来。
  3. 对所有的乘积字符串进行字符串加法运算。(融合操作类似于合并 k 个有序链表,可以自行优化或博客

代码挺长,但很简单。

深入学习这种解法也能学到不少东西,包括字符串加法,合并 k 个有序链表的应用。

代码
class Solution {
    public String multiply(String num1, String num2) {
        if(num1 == null || num1.length() == 0) return "0";
        if("0".equals(num1) || "0".equals(num2)) return "0";
        
        String[] results = new String[num1.length()];
        for(int i = num1.length() - 1; i >= 0; i --) {
            int n1Val = num1.charAt(i) - '0';
            int carry = 0;
            StringBuilder cur = new StringBuilder();
            for(int j = num2.length() - 1; j >= 0; j --) {
                int n2Val = num2.charAt(j) - '0';
                int sum = carry + n1Val * n2Val;
                carry = sum / 10;
                cur.append(sum % 10);
            }
            
            cur.append(carry != 0 ? carry : "");
            results[i] = cur.reverse().append(generateZero(num1.length() - i - 1)).toString();
        }
        
        // 这一部分融合操作可以优化成 分治 算法,效率更高。
        // 类似于合并 k 个有序链表。
        // 这里简单起见,我并没有优化,感兴趣的同学可以做一下优化。
        String res = results[0];
        for(int i = 1; i < results.length; i ++) {
            res = addStrings(res, results[i]);
        }
        
        return res;
    }
    
    private String generateZero(int n) {
        char[] zeros = new char[n];
        Arrays.fill(zeros, '0');
        return new String(zeros);
    }
    
    private String addStrings(String num1, String num2) {
        StringBuilder res = new StringBuilder();
        int carry = 0;
        int i = num1.length() - 1;
        int j = num2.length() - 1;
        while(i >= 0 || j >= 0 || carry != 0) {
            int num1Val = i >= 0 ? num1.charAt(i) - '0' : 0;
            int num2Val = j >= 0 ? num2.charAt(j) - '0' : 0;
            int sum = carry + num1Val + num2Val;
            carry = sum / 10;
            
            res.append(sum % 10);
            
            if(i >= 0) i --;
            if(j >= 0) j --;
        }
        
        return res.reverse().toString();
    }
}
结果

在这里插入图片描述

解法二:竖式乘法

首先需要知道一点:两个数字相乘,最后的结果的长度必然小于两者的长度之和。

竖式乘法就是采用的这种思想,相当于将结果整个采用数组的方式存储。

手画了一个简单的图,画的粗糙,见谅 :)
在这里插入图片描述
图中结果数组中的第 k 位在哪些情况下会被修改呢?

现在我们假设 num1 当前的位置是 i ,num2 当前的位置是 j 。

那么可以看出:i + j + 1 == k 时第 k 位会被直接修改,同时也可能会进位第 k - 1 位。

具体情况如下:

  1. 结果的 第 k 位 上的值就是(原来第 k 位的值 ret[k] + 增量 num1[i] * num2[j] )之和的余数。
  2. 结果的 第 k - 1 位,可能会得到 ret[k] + num1[i] * num2[j] 运算产生的进位。
代码
class Solution {
    public String multiply(String num1, String num2) {
        
        char[] ret = new char[num1.length() + num2.length()];
        Arrays.fill(ret, '0');
        
        for(int i = num1.length() - 1; i >= 0; i --) {
            int num1Val = num1.charAt(i) - '0';
            for(int j = num2.length() - 1; j >= 0; j --) {
                int num2Val = num2.charAt(j) - '0';
                int sum = (ret[i + j + 1] - '0') + num1Val * num2Val;
                ret[i + j + 1] = (char)(sum % 10 + '0');
                ret[i + j] += sum / 10;
            }
        }
        
        for(int i = 0; i < ret.length; i ++) {
            if(ret[i] != '0' || i == ret.length - 1) {
                return new String(ret, i, ret.length - i);
            }
        }
        
        return "0";
    }
}
结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值