leetcode
leetcode 1 ~ 50
字符串相乘

字符串相乘

难度:

标签:

题目描述

给定两个以字符串形式表示的非负整数 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本身。

代码结果

运行时间: 144 ms, 内存: 15.1 MB


/*
 * 思路:
 * 1. 我们可以使用IntStream来遍历两个字符串的每一位,从右往左逐位相乘。
 * 2. 使用平铺的lambda表达式来模拟双重循环和乘法的累加。
 * 3. 最终将结果数组中的每个元素转换为字符,合并成最终结果。
 */
import java.util.stream.IntStream;
public class Solution {
    public String multiply(String num1, String num2) {
        int m = num1.length(), n = num2.length();
        int[] result = new int[m + n];
        IntStream.range(0, m).forEach(i -> {
            IntStream.range(0, n).forEach(j -> {
                int mul = (num1.charAt(m - 1 - i) - '0') * (num2.charAt(n - 1 - j) - '0');
                int sum = mul + result[i + j];
                result[i + j] = sum % 10;
                result[i + j + 1] += sum / 10;
            });
        });
        StringBuilder sb = new StringBuilder();
        IntStream.range(0, result.length)
                .map(i -> result[result.length - 1 - i])
                .dropWhile(i -> i == 0)
                .forEach(sb::append);
        return sb.length() == 0 ? "0" : sb.toString();
    }
}

解释

方法:

这个题解使用了模拟竖式乘法的方法来计算两个字符串表示的数字相乘的结果。具体做法是: 1. 如果其中一个数字是0,直接返回'0'。 2. 创建一个长度为 m+n 的数组 ans 用于存储计算结果的每一位。 3. 从右往左遍历 num1 和 num2 的每个数字,将对应位置的数字相乘,再加上 ans 数组中对应位置的值。 4. 将乘积的个位数存入 ans 数组的 p2 位置,将十位数加到 p1 位置。 5. 遍历完成后,将 ans 数组转换为字符串,并去除前导0(如果有)。

时间复杂度:

O(m*n)

空间复杂度:

O(m+n)

代码细节讲解

🦆
为什么在遍历num1和num2的时候选择从右向左遍历,而不是从左向右?
选择从右向左遍历num1和num2是因为这种方式模拟了传统手工乘法的过程。在手工乘法中,我们通常从最低位(右侧)开始计算,逐步向左移动。这样做可以方便地处理进位问题,因为每次乘积只影响当前位和其左侧的一位(即进位位)。如果从左向右遍历,那么处理进位时需要不断调整之前计算的结果,逻辑会更复杂。
🦆
在计算每一位的乘积后,为什么需要即时加上数组ans中对应位置的值?这种累加的处理对结果的正确性有什么影响?
每次计算两个数字的某一位的乘积后,需要加上数组ans中对应位置的值,是因为ans数组是用来累积和保存最终的乘积结果的。在乘法中,多个位的乘积可能会影响到同一位置的结果(比如重叠的部分)。即时加上ans数组中的值可以保证这些影响被正确地累加到最终结果中。这种累加的处理保证了每一步的中间结果都正确地反映了之前所有乘积的累积效果,从而保证了最终结果的正确性。
🦆
你如何处理乘积结果数组ans中的进位?具体是如何确定进位的十位数和个位数分别添加到哪个位置的?
处理乘积结果数组ans中的进位是通过计算每次乘积的十位数和个位数来实现的。具体方法是:将乘积加上当前ans数组对应位置的值后,使用整除10的操作得到十位数(进位),使用取余10的操作得到个位数。然后,将十位数(进位)加到当前位置的前一位(p1位置),个位数更新到当前位置(p2位置)。这样可以保证每次乘积的进位都被正确处理,同时更新对应位置的值。
🦆
在去除前导零的过程中,如果所有位都是零(即结果为0),这种情况是如何处理的?
在去除前导零的过程中,如果遍历完整个结果数组ans后发现所有位都是零,这表明乘积结果为0。处理这种情况的方法是,在将ans数组转换为字符串之前,检查是否所有位都为零(即从数组的起始到终止都没有找到非零元素)。如果是这样,直接返回字符串'0'。这样确保了即使输入的字符串表示的数字非常大,其乘积结果为0时,也能正确地返回'0'。

相关问题

两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

 

示例 1:

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

 

提示:

  • 每个链表中的节点数在范围 [1, 100]
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零

加一

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

 

示例 1:

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。

示例 2:

输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。

示例 3:

输入:digits = [0]
输出:[1]

 

提示:

  • 1 <= digits.length <= 100
  • 0 <= digits[i] <= 9

二进制求和

给你两个二进制字符串 ab ,以二进制字符串的形式返回它们的和。

 

示例 1:

输入:a = "11", b = "1"
输出:"100"

示例 2:

输入:a = "1010", b = "1011"
输出:"10101"

 

提示:

  • 1 <= a.length, b.length <= 104
  • ab 仅由字符 '0''1' 组成
  • 字符串如果不是 "0" ,就不含前导零

字符串相加

给定两个字符串形式的非负整数 num1num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

 

示例 1:

输入:num1 = "11", num2 = "123"
输出:"134"

示例 2:

输入:num1 = "456", num2 = "77"
输出:"533"

示例 3:

输入:num1 = "0", num2 = "0"
输出:"0"

 

 

提示:

  • 1 <= num1.length, num2.length <= 104
  • num1num2 都只包含数字 0-9
  • num1num2 都不包含任何前导零