题干信息
思路(区别于传统竖式)
- 使用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=0∑Naaixi, B ( x ) = ∑ i = 0 N b b i x i B(x) = \sum_{i = 0}^{N_b} b_ix^i B(x)=i=0∑Nbbixi
- 任何两个数相当于对 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=0∑Na+Nbcixi,其中
c
i
=
∑
k
=
0
i
a
k
b
i
−
k
c_i = \sum_{k = 0}^i a_k b_{i - k}
ci=∑k=0iakbi−k
- 要得到 c i c_i ci的结果,只需要考察 A ( x ) A(x) A(x)和 B ( x ) B(x) B(x)中哪些次幂能够加起来等于结果中的 i i i即可。
- 多项式乘积的快速算法(FFT)见算法导论第30章