超大数相加和超大数相乘(即字符串相加leetcode415和字符串相乘leetcode43)

本文深入探讨了两个非负整数以字符串形式表示时的数学运算问题,包括加法和乘法的实现。通过详细的代码示例,展示了如何避免使用内置的大数类型或直接转换为整数,提供了一种新颖的解决思路。

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

示例 1:

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。

注意:

num1 和num2 的长度都小于 5100.
num1 和num2 都只包含数字 0-9.
num1 和num2 都不包含任何前导零。
你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式。

解题思路(来自leetcode):
在这里插入图片描述
代码:

class Solution:
    def addStrings(self, num1: str, num2: str) -> str:
        i, j, carry = len(num1) - 1, len(num2) - 1, 0
        res = ""
        while i >= 0 or j >= 0:
            n1 = int(num1[i]) if i >= 0 else 0
            n2 = int(num2[j]) if j >= 0 else 0
            tmp = n1 + n2 + carry
            carry = tmp //10
            res = str(tmp % 10) + res
            i, j = i-1, j-1
        return "1" + res if carry else res

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)或直接将输入转换为整数来处理。

在这里插入图片描述在这里插入图片描述
python代码:
来自https://leetcode-cn.com/problems/multiply-strings/solution/python-zi-fu-chuan-bao-li-mo-ni-shu-shi-cheng-fa-j/

class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        if num1 == "0" or num2 == "0":
            return "0"
        l1, l2 = len(num1), len(num2)
        if l1 < l2:
            num1, num2 = num2, num1
            l1, l2 = l2, l1
        num2 = num2[::-1]
        res = "0"
        for i, digit in enumerate(num2):
            tmp = self.stringMultiplyDigit(num1, int(digit)) + "0" * i
            res = self.stringPlusstring(res, tmp)
        return res
    
    def stringMultiplyDigit(self, string, n):
        s = string[::-1]
        res = []
        for i,char in enumerate(s):
            num = int(char)
            res.append(n*num)
        res = self.carrySolver(res)
        res = res[::-1]
        return "".join(str(x) for x in res)
    
    def carrySolver(self, nums):
        i = 0 
        while i < len(nums):
            if nums[i] >= 10:
                carry = nums[i] // 10
                if i == len(nums) - 1:
                    nums.append(carry)
                else:
                    nums[i+1] += carry
                nums[i] %= 10
            i += 1
        return nums
    def stringPlusstring(self, string1, string2):
        s1 , s2 = string1, string2
        if len(s1) < len(s2):
            s1, s2 = s2, s1
        s1 = [int(x) for x in s1]
        s2 = [int(x) for x in s2]
        s1 = s1[::-1]
        s2 = s2[::-1]
        for i, digit in enumerate(s2):
            s1[i] += s2[i]
        s1 = self.carrySolver(s1)
        s1 = s1[::-1]
        return "".join(str(x) for x in s1)
力扣第43题 **“字符串相乘”** 是一道经典的大数乘法问题,要求不使用内置大数库或直接将字符串转为整来计算乘积。 --- ## 🟩 题目描述(LeetCode 43. 字符串相乘) 给你两个非负整字符串 `num1` `num2`,返回它们的乘积,也表示为字符串。 > **注意:不能使用任何内置 BigInteger 库,也不能直接将输入字符串转成整。** ### 示例: ```cpp 输入: num1 = "12", num2 = "34" 输出: "408" ``` --- ## ✅ 解法思路:模拟竖式乘法 我们模仿手算乘法的过程: ``` 1 2 × 3 4 ----- 4 8 ← 4×12 3 6 ← 3×12,左移一位(即补一个0) ------- 4 0 8 ``` 步骤分解: 1. 从右往左遍历 `num2` 的每一位。 2. 用该位去乘整个 `num1`,得到一个部分积。 3. 每次部分积要根据位置 **补零对齐**(第 i 位补 `n-i` 个 '0')。 4. 所有部分积累加起来。 --- ## ✅ 正确 C++ 实现(带详细注释) ```cpp class Solution { public: string multiply(string num1, string num2) { // 特殊情况:任意 × 0 = 0 if (num1 == "0" || num2 == "0") return "0"; int m = num1.size() - 1; int n = num2.size() - 1; string result = ""; // 最终结果 // 外层循环:遍历 num2 的每一位(从低位到高位) for (int i = n; i >= 0; i--) { string curr = ""; int a = num2[i] - '0'; // 当前 digit int add = 0; // 进位 // 补零:当前是第 i 位,需补 (n - i) 个 '0' for (int k = n; k > i; k--) { curr.push_back('0'); } // 内层循环:a × num1 的每一位 for (int j = m; j >= 0; j--) { int b = num1[j] - '0'; int temp = a * b + add; // 中间值 curr.push_back('0' + (temp % 10)); // 加入当前位 add = temp / 10; // 更新进位 } // 处理剩余进位 while (add) { curr.push_back('0' + (add % 10)); add /= 10; } // 反转当前部分积(因为是从低位开始算的) reverse(curr.begin(), curr.end()); // 累加到最终结果 result = addStrings(result, curr); } return result; } // 辅助函:两个字符串相加 string addStrings(string num1, string num2) { int i = num1.size() - 1; int j = num2.size() - 1; int carry = 0; string res = ""; while (i >= 0 || j >= 0 || carry) { int x = (i >= 0) ? num1[i--] - '0' : 0; int y = (j >= 0) ? num2[j--] - '0' : 0; int sum = x + y + carry; res.push_back('0' + (sum % 10)); carry = sum / 10; } reverse(res.begin(), res.end()); return res; } }; ``` --- ## 🔍 关键点解析 | 步骤 | 说明 | |------|------| | `a = num2[i] - '0'` | 将字符转为字 | | 补零逻辑 | 第 `i` 位对应的是 $10^{n-i}$ 位,所以补 `(n-i)` 个零 | | `temp = a*b + add` | 先保留完整值,再取模除法 | | `while(add)` | 确保所有进位都被处理(比如进位是 123 要变成 "321" 再反转) | | `reverse(curr)` | 因为我们是从个位开始写的,顺序反了,必须反转 | | `addStrings()` | 自实现字符串加法 | --- ## ✅ 时间复杂度分析 - 设 `len(num1)=m`, `len(num2)=n` - 外层循环 `n` 次 - 每次内层乘法耗时 `O(m)` - 每次加法耗时 `O(max(len(result), len(curr)))`,最坏 `O(m+n)` - 总时间复杂度:**O(m×n + (m+n)) ≈ O(m×n)** 空间复杂度:**O(m+n)**(结果最长是 `m+n` 位) --- ## ✅ 测试样例验证 | 输入 | 输出 | |------|------| | `"12"`, `"34"` | `"408"` ✅ | | `"9"`, `"9"` | `"81"` ✅ | | `"0"`, `"123"` | `"0"` ✅ | | `"999"`, `"999"` | `"998001"` ✅ | --- ## 💡 提升建议(进阶优化) 你还可以尝试以下方法提升效率: ### 方法二:使用组存储结果(推荐) 不生成多个中间字符串,而是用一个组 `pos` 存储每一位的乘积结果,最后统一处理进位。 ```cpp string multiply(vector<int>& num1, vector<int>& num2) { int m = num1.size(), n = num2.size(); vector<int> pos(m + n, 0); for (int i = m - 1; i >= 0; i--) { for (int j = n - 1; j >= 0; j--) { int mul = (num1[i] - '0') * (num2[j] - '0'); int p1 = i + j, p2 = i + j + 1; int sum = mul + pos[p2]; pos[p2] = sum % 10; pos[p1] += sum / 10; } } string res = ""; for (int p : pos) { if (!(res.empty() && p == 0)) { // 去前导零 res.push_back('0' + p); } } return res.empty() ? "0" : res; } ``` 👉 这种方法避免了多次字符串加法,效率更高。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值