43. 字符串相乘

题目链接:力扣

 解题思路:题目要求不能使用任何内置的 BigInteger 库或直接将输入转换为整数。所以可以模拟小学学过的竖式乘法进行运算。比如123*456

    1 2 3
    4 5 6
---------
    7 3 8
  6 1 5
4 9 2
---------
5 6 0 8 8

 可以将乘数456的每一位与被乘数123的每一位相乘,然后将相乘的结果累加,注意每次累加之前都需要将加数向左移动一位与被加数相加。比如被加数738和加数615相加,需要将加数615左移一位,相当于加数最右边补0变为6150与738相加。

具体的算法步骤如下:

  1. 如过num1或者num2其中任意一个等于"0",直接返回字符串"0"
  2. result="0"保存结果,carry保存进位(满10进1),preZero=0保存加数左移的位数,每轮循环后自增1
  3. 从右往左循环遍历num2的每一位:
    1. builder=new StringBuilder()保存相乘的结果
    2. builder中追加perZero个0(相当于最右边加数补0)
    3. x = num2.charAt(i) - '0',被乘数的当前位
    4. 从右往左循环遍历num1的每一位:
      1. y = num1.charAt(j) - '0' , 乘数的当前位
      2. 本轮相乘的结果multi = x*y+carry
      3. carry = multi/10;
      4. builder.append(multi%10)
    5. 内层循环结束后,如果carry不等于0,需要把进位加上,builder.append(carry),carry=0
    6. add(result,builder.toString),两个字符串相加的函数,注意:这里的builder.toString的结果为低位在前,高位在后
    7. preZero++,每一轮加数都要比上一轮加数大10被,即多补一个0
  4. new StringBuilder(result).reverse().toString()即所求

两个字符串相加add(String first,String second)函数实现:

  1. 因为传递过来的fist和second都是低位在前,高位在后,所以只需要从左往右遍历first和second字符串的每一位,令其相加,满10进1就可以了,循环的次数为first和second字符串长度的最大值,如果当前遍历的索引超过了其中一个字符串的长度,这个较短的字符串相当于贡献的加数为0,这里注意以下就可以了,避免索引越界

AC代码:

public class Solution {

    public static String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        String result ="0";
        int carry = 0;
        int preZero = 0;
        for (int i = num2.length() - 1; i >= 0; i--) {
            StringBuilder builder = new StringBuilder();
            for (int k = 0; k < preZero; k++) {
                builder.append("0");
            }
            int x = (num2.charAt(i) - '0');
            for (int j = num1.length() - 1; j >= 0; j--) {
                int y = (num1.charAt(j) - '0');
                int multi = x * y + carry;
                carry = multi / 10;
                builder.append(multi % 10);
            }
            if (carry != 0) {
                builder.append(carry);
                carry = 0;
            }
            result = add(result, builder.toString());
            preZero++;
        }

        return new StringBuilder(result).reverse().toString();
    }

    public static String add(String first, String second) {
        int firstLen = first.length();
        int secondLen = second.length();
        StringBuilder result = new StringBuilder();
        int maxLen = Math.max(firstLen, secondLen);
        int carry = 0;
        for (int i = 0; i < maxLen; i++) {
            int x = i >= firstLen ? 0 : first.charAt(i) - '0';
            int y = i >= secondLen ? 0 : second.charAt(i) - '0';
            int add = x+y+carry;
            carry=add/10;
            result.append(add%10);
        }
        if (carry!=0){
            result.append(carry);
        }
        return result.toString();
    }
}

优化:上述解法中有较多的字符串相加操作,效率比较低,可以使用一个int数组保存最终的结果的每一位

使用数组保存最终的结果时所需要的两个前提:

  1. 乘数num2的位数为m,被乘数num1的位数为n,则num1*num2的结果的最大位数为m+n,最小位数为m+m-1
    1. 比如m=2,n=2时,num1和num2都取最小值10,则num1*num2=100,相乘结果位数为3,都取最大值时99*99=9801,结果位数为4,所以最大位数为m+n,最小值为m+n-1。
    2. 所以结果数组res的数组长度设置为m+n一定是可以,要么全部使用,要么只会浪费一个空间
  2. num1[i] * num2[j] 的结果 tem 在res存储的位置是固定的(假设从结果数组res的最后一位开始存储,低位存储在最右边),tem的位数只会有两种形式,要么一位,要么是两位,其中一位可以看作两位的特殊形式,高位补为0(即,0y或者xy),其中个数存储在res[i+j+1]中,十位存储在res[i+j]中,在存储的时候,需要与原位置中的数值进行相加,如下所示
                   1 | 2 | 3 |
                 * 4 | 5 | 6 |
                 --------------
                   7 | 3 | 8 |
               6 | 1 | 5 |
           4 | 9 | 2 |
          --------------------
           5 | 6 | 0 | 8 | 8
res索引[0   1   2   3   4   5]

 比如num1[2]=3,num2[2]=6,num1[2]*num2[2]=18,结果18的个位8保存在res[2+2+1],即res[5]中,十位1保存在res[4]中,这里的1会和其他保存在res[4]的结果相加,最终res[4]=8

最终结果就是res数组转为字符串,注意如果res[0]=0,要去除前导0

AC代码

class Solution {
    public static 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 = num2.length() - 1; i >= 0; i--) {
            int x = num2.charAt(i) - '0';
            for (int j = num1.length() - 1; j >= 0; j--) {
                int y = num1.charAt(j) - '0';
                int result = res[i + j + 1] + x * y;
                res[i + j + 1] = result % 10;
                res[i + j] += result / 10;
            }
        }
        int start = res[0] != 0 ? 0 : 1;
        StringBuilder builder = new StringBuilder();
        for (int i =start;i<res.length;i++){
            builder.append(res[i]);
        }
        return builder.toString();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值