数据结构与算法之深入解析“字符串相乘”的求解思路与算法示例

Posted Serendipity·y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法之深入解析“字符串相乘”的求解思路与算法示例相关的知识,希望对你有一定的参考价值。

一、题目要求

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

二、求解算法

① 普通竖式

  • 竖式运算思想,以 num1 为 123,num2 为 456 为例分析:

class Solution 
    /**
    * 计算形式
    *    num1
    *  x num2
    *  ------
    *  result
    */
    public String multiply(String num1, String num2) 
        if (num1.equals("0") || num2.equals("0")) 
            return "0";
        
        // 保存计算结果
        String res = "0";
        
        // num2 逐位与 num1 相乘
        for (int i = num2.length() - 1; i >= 0; i--) 
            int carry = 0;
            // 保存 num2 第i位数字与 num1 相乘的结果
            StringBuilder temp = new StringBuilder();
            // 补 0 
            for (int j = 0; j < num2.length() - 1 - i; j++) 
                temp.append(0);
            
            int n2 = num2.charAt(i) - '0';
            
            // num2 的第 i 位数字 n2 与 num1 相乘
            for (int j = num1.length() - 1; j >= 0 || carry != 0; j--) 
                int n1 = j < 0 ? 0 : num1.charAt(j) - '0';
                int product = (n1 * n2 + carry) % 10;
                temp.append(product);
                carry = (n1 * n2 + carry) / 10;
            
            // 将当前结果与新计算的结果求和作为新的结果
            res = addStrings(res, temp.reverse().toString());
        
        return res;
    

    /**
     * 对两个字符串数字进行相加,返回字符串形式的和
     */
    public String addStrings(String num1, String num2) 
        StringBuilder builder = new StringBuilder();
        int carry = 0;
        for (int i = num1.length() - 1, j = num2.length() - 1;
             i >= 0 || j >= 0 || carry != 0;
             i--, j--) 
            int x = i < 0 ? 0 : num1.charAt(i) - '0';
            int y = j < 0 ? 0 : num2.charAt(j) - '0';
            int sum = (x + y + carry) % 10;
            builder.append(sum);
            carry = (x + y + carry) / 10;
        
        return builder.reverse().toString();
    

② 优化竖式

  • 该算法是通过两数相乘时,乘数某位与被乘数某位相乘,与产生结果的位置的规律来完成。
  • 具体规律如下:
    • 乘数 num1 位数为 M,被乘数 num2 位数为 N, num1 x num2 结果 res 最大总位数为 M+N;
    • num1[i] x num2[j] 的结果为 tmp(位数为两位,“0x”、"xy"的形式),其第一位位于 res[i+j],第二位位于 res[i+j+1]。

  • Java 示例:
class Solution 
    public String multiply(String num1, String num2) 
        if (num1.equals("0") || num2.equals("0")) 
            return "0";
        
        int[] res = new int[num1.length() + num2.length()];
        for (int i = num1.length() - 1; i >= 0; i--) 
            int n1 = num1.charAt(i) - '0';
            for (int j = num2.length() - 1; j >= 0; j--) 
                int n2 = num2.charAt(j) - '0';
                int sum = (res[i + j + 1] + n1 * n2);
                res[i + j + 1] = sum % 10;
                res[i + j] += sum / 10;
            
        

        StringBuilder result = new StringBuilder();
        for (int i = 0; i < res.length; i++) 
            if (i == 0 && res[i] == 0) continue;
            result.append(res[i]);
        
        return result.toString();
    

③ 做乘法(LeetCode 官方解法)

  • 加法做法是从右往左遍历乘数,将乘数的每一位与被乘数相乘得到对应的结果,再将每次得到的结果累加,整个过程中涉及到较多字符串相加的操作。如果使用数组代替字符串存储结果,则可以减少对字符串的操作。
  • 令 m 和 n 分别表示 num1 和 num2 的长度,并且它们均不为 0,则 num1 和 num2 的乘积的长度为 m+n−1 或 m+n。简单证明如下:
    • 如果 num1 和 num2 都取最小值,则 num1 = 10m−1,num2 = 10n−1,num1 × num2 = 10m+n−2,乘积的长度为 m+n−1;
    • 如果 num1 和 num2 都取最大值,则 num1 =10m−1,num2 = 10n−1,num1 × num2 = 10m+n−10m−10n+1,乘积显然小于 10m+n 且大于 10m+n−1,因此乘积的长度为 m+n。
  • 由于 num1 和 num2 的乘积的最大长度为 m+n,因此创建长度为 m+n 的数组 ansArr 用于存储乘积。对于任意 0≤i<m 和 0≤j<n,num1[i]×num2[j] 的结果位于 ansArr[i+j+1],如果 ansArr[i+j+1]≥10,则将进位部分加到 ansArr[i+j]。
  • 最后,将数组 ansArr 转成字符串,如果最高位是 0 则舍弃最高位。

  • Java 示例:
class Solution 
    public String multiply(String num1, String num2) 
        if (num1.equals("0") || num2.equals("0")) 
            return "0";
        
        int m = num1.length(), n = num2.length();
        int[] ansArr = new int[m + n];
        for (int i = m - 1; i >= 0; i--) 
            int x = num1.charAt(i) - '0';
            for (int j = n - 1; j >= 0; j--) 
                int y = num2.charAt(j) - '0';
                ansArr[i + j + 1] += x * y;
            
        
        for (int i = m + n - 1; i > 0; i--) 
            ansArr[i - 1] += ansArr[i] / 10;
            ansArr[i] %= 10;
        
        int index = ansArr[0] == 0 ? 1 : 0;
        StringBuffer ans = new StringBuffer();
        while (index < m + n) 
            ans.append(ansArr[index]);
            index++;
        
        return ans.toString();
    

  • C++ 示例:
class Solution 
public:
    string multiply(string num1, string num2) 
        if (num1 == "0" || num2 == "0") 
            return "0";
        
        int m = num1.size(), n = num2.size();
        auto ansArr = vector<int>(m + n);
        for (int i = m - 1; i >= 0; i--) 
            int x = num1.at(i) - '0';
            for (int j = n - 1; j >= 0; j--) 
                int y = num2.at(j) - '0';
                ansArr[i + j + 1] += x * y;
            
        
        for (int i = m + n - 1; i > 0; i--) 
            ansArr[i - 1] += ansArr[i] / 10;
            ansArr[i] %= 10;
        
        int index = ansArr[0] == 0 ? 1 : 0;
        string ans;
        while (index < m + n) 
            ans.push_back(ansArr[index]);
            index++;
        
        for (auto &c: ans) 
            c += '0';
        
        return ans;
    
;

以上是关于数据结构与算法之深入解析“字符串相乘”的求解思路与算法示例的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法之深入解析“股票的最大利润”的求解思路与算法示例

数据结构与算法之深入解析“安装栅栏”的求解思路与算法示例

数据结构与算法之深入解析“最长连续序列”的求解思路与算法示例

数据结构与算法之深入解析“路径总和”的求解思路与算法示例

数据结构与算法之深入解析“斐波那契数”的求解思路与算法示例

数据结构与算法之深入解析“连续整数求和”的求解思路与算法示例