leetcode 字符串相乘

题干信息

在这里插入图片描述在这里插入图片描述在这里插入图片描述

思路(区别于传统竖式)

  • 使用arr数组
  • 使用竖式乘法完成例如上图所示的例子
    • 将567分成500 + 60 + 7
    • 分别将500,60,7与1234的每一位相乘
    • 将相乘结果按照实际的偏移加入数组即可。
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

我的代码(还有优化空间-执行时间6ms)

public String multiply(String num1, String num2) {
        if(num1.equals("0") || num2.equals("0"))
            return "0";
        if(num2.length() > num1.length())
            return multiply(num2,num1);
        int len_1 = num1.length();
        int len_2 = num2.length();
        char[] arr = new char[len_1 + len_2];
        Arrays.fill(arr,'0');
        for(int i = len_1 - 1;i>=0;i--)
        {
            for(int j = len_2 - 1;j>=0;j--)
            {
                int start = i + j + 1;  //从arr的最低位开始相加
                int carry = 0;
                int k = start;
                int temp = (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
                while(temp!=0)
                {
                    int result = (arr[k] - '0') + temp % 10 + carry;
                    arr[k] = (char)((result % 10) + '0');
                    carry = result / 10;
                    k--;
                    temp /= 10;
                }
                while(carry!=0)
                {
                    int result = (arr[k] - '0') + carry;
                    arr[k] = (char)((result % 10) + '0');
                    carry = result / 10;
                    k--;
                }
            }
        }
        int j;
        for(j = 0;j<arr.length;j++)
        {
            if(arr[j]!='0')
                break;
        }
        return new String(arr,j,arr.length - j);
    }

优化思路

  • 上述算法中每次进位时都将结果往前累加
  • 能否不将进位进行累加,而将结果就存在当前位中,然后最后一次性进行进位方面的处理呢?
    • 可以!
    • 根据传统竖式乘法,即使是200个9组成的数乘以200个9组成的数,每一位的中间结果也不会超出整数的范围
    • 这可以使得时间复杂度降低到 O ( m n ) O(mn) O(mn)

优化后的代码1(3ms)

public String multiply(String num1, String num2) {
        if(num1.equals("0") || num2.equals("0"))
            return "0";
        if(num2.length() > num1.length())
            return multiply(num2,num1);
        int len_1 = num1.length();
        int len_2 = num2.length();
        int[] arr = new int[len_1 + len_2];
        Arrays.fill(arr,0);
        //进位全部保存在当前位中
        for(int i = len_1 - 1;i>=0;i--)
        {
            for(int j = len_2 - 1;j>=0;j--)
            {
                int k = i + j + 1;
                int temp = (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
                while(temp!=0)
                {
                    arr[k] += temp % 10;
                    k--;
                    temp /= 10;
                }
            }
        }
        char[] arr1 = new char[len_1 + len_2];
        //一次性进位,优秀!
        for(int i = arr.length - 1;i>=0;i--)
        {
            arr1[i] = (char)((arr[i] % 10) +  '0');
            arr[i - 1] += arr[i] / 10;
        }
        int j;
        for(j = 0;j<arr1.length;j++)
        {
            if(arr1[j]!='0')
                break;
        }
        return new String(arr1,j,arr1.length - j);
    }

优化后的代码2(官方代码2ms)

  • 上述代码中不去进位的部分中的temp仍然是逐位进行累加的
  • 能否就将结果保存在最低位呢,就将结果放在i + j + 1的位置?
    • 可以
    • 很多个9*9 = 81相加才会越界
class Solution {
    public String multiply(String num1, String num2) {
        if(num1.equals("0") || num2.equals("0"))
            return "0";
        if(num2.length() > num1.length())
            return multiply(num2,num1);
        int len_1 = num1.length();
        int len_2 = num2.length();
        int[] arr = new int[len_1 + len_2];
        Arrays.fill(arr,0);
        //进位全部保存在当前位中
        for(int i = len_1 - 1;i>=0;i--)
        {
            for(int j = len_2 - 1;j>=0;j--)
            {
                int k = i + j + 1;
                int temp = (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
                arr[k] += temp;

            }
        }
        char[] arr1 = new char[len_1 + len_2];
        //一次性进位+最低位保存,优秀!
        for(int i = arr.length - 1;i>=0;i--)
        {
            arr1[i] = (char)((arr[i] % 10) +  '0');
            if(i!=0)
                arr[i - 1] += arr[i] / 10;
        }
        int j;
        for(j = 0;j<arr1.length;j++)
        {
            if(arr1[j]!='0')
                break;
        }
        return new String(arr1,j,arr1.length - j);
    }

}

与多项式求和,FFT的联系

当时没有想到这道题居然还能和FFT产生联系。

  • 考察多项式 A ( x ) = ∑ i = 0 N a a i x i A(x) = \sum_{i = 0}^{N_a} a_ix^i A(x)=i=0Naaixi, B ( x ) = ∑ i = 0 N b b i x i B(x) = \sum_{i = 0}^{N_b} b_ix^i B(x)=i=0Nbbixi
  • 任何两个数相当于对 A ( x ) A(x) A(x) B ( x ) B(x) B(x) x = 10 x = 10 x=10处进行eval。
  • 于是最终的结果为两个多项式相乘得到的结果在 x = 10 x = 10 x=10处进行eval
  • 很容易得出相乘后的多项式为 C ( x ) = ∑ i = 0 N a + N b c i x i C(x) = \sum_{i = 0}^{N_a + N_b} c_i x^i C(x)=i=0Na+Nbcixi,其中 c i = ∑ k = 0 i a k b i − k c_i = \sum_{k = 0}^i a_k b_{i - k} ci=k=0iakbik
    • 要得到 c i c_i ci的结果,只需要考察 A ( x ) A(x) A(x) B ( x ) B(x) B(x)中哪些次幂能够加起来等于结果中的 i i i即可。
  • 多项式乘积的快速算法(FFT)见算法导论第30章
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值