[LeetCode#43]Multiply Strings

本文探讨了两个大数字符串相乘的问题,并给出了两种解决方案。第一种为直观的逐位相乘再累加的方法;第二种则更加高效简洁,通过逆序输入、按位相乘并直接构建结果数组的方式避免了多次字符串操作。

Problem:

Given two numbers represented as strings, return multiplication of the numbers as a string.

Note: The numbers can be arbitrarily large and are non-negative.

Analysis:

The naive solution of this problem is to following the routine of multiplication, whose algorithm we are quite familar with. 
Solution 1: (Naive solution)
1. Iterate on one string's digit, use the digit multiply with the other number.
2. Add the partial result with total result. (note the trailing zero of each mulitiplication). 

Although the algorithm is clear, but it involves many mainpulations over string. I have made many mistakes in my first implementation.

Wrong solution:

public class Solution {
    public String multiply(String num1, String num2) {
        if (num1 == null || num2 == null)
            throw new IllegalArgumentException("The passed in arguments is illegal!");
        char[] num2_array = num2.toCharArray(); 
        String ret = "0";
        for (int i = num2_array.length - 1; i >= 0; i--) {
            String temp = multiple(num1, num2_array[i]);
            ret = add(temp + ('0'*num2_array.length-1-i), ret);
        }
        return ret;
    }
    
    private String multiple(String num_str, Character n) {
        int carry = 0;
        char[] num = num_str.toCharArray();
        StringBuffer ret = new StringBuffer();
        for (int i = num.length - 1; i >= 0; i--) {
            int temp = Integer.valueOf(num[i]) * Integer.valueOf(n) + carry;
            int cur_digit = temp % 10;
            carry = temp / 10;
            ret.insert(0, cur_digit);
        }
        return ret.toString();
    }
    
    private String add(String num1_str, String num2_str) {
        char[] num1 = num1_str.toCharArray();
        char[] num2 = num2_str.toCharArray();
        int carry = 0;
        int len = Math.max(num1.length, num2.length);
        StringBuffer ret = new StringBuffer();
        
        for(int i = 0; i < len; i++) {
            int temp1 = (num1.length-1 >= i) ? Integer.valueOf(num1[i]) : 0;
            int temp2 = (num2.length-1 >= i) ? Integer.valueOf(num2[i]) : 0;
            int temp = temp1 + temp2 + carry;
            int cur_digit = temp % 10;
            carry = temp / 10;
            ret.insert(0, cur_digit);
        }
        return ret.toString();
    }
}

Mistakes analysis:

*****************************************************************************
Mistakes
mistake 1: forget to add carray number, when loop is over.
for (int i = num.length - 1; i >= 0; i--) {
    ...
    ret.insert(0, cur_digit);
}
return ret.toString();
? What if the carry is not 0.
Fix:
for (int i = num.length - 1; i >= 0; i--) {
    ...
}
if (carry != 0)
    ret.insert(0, carry);
return ret.toString();

mistake 2: use Integer.valueOf() against character rather than String. 
int temp = Integer.valueOf(num[i]) * Integer.valueOf(n) + carry;
Integer.valueOf(character c) would return the Ascii code of character c, rather than the Integer form with the value: c.
Fix: covert the character into a string.
int temp = Integer.valueOf(num[i]+"") * Integer.valueOf(n+"") + carry;

mistake 3: use Ruby code for the implemetation. wish '0' * 3 could return "000"
ret = add(temp + ('0'*num2_array.length-1-i), ret);
Fix: use a while loop to add trial zero.
while (trail_zero < num2_array.length-1-i) {
    temp += '0';
    trail_zero++;
}

mistake 4: the index of add is wrong, since the two string may be in different length, and we must start from the right digt to left digit. 
Start from left is absolutely wrong!
for(int i = 0; i < len; i++) {
    int temp1 = (num1.length-1 >= i) ? Integer.valueOf(num1[i]) : 0;
    int temp2 = (num2.length-1 >= i) ? Integer.valueOf(num2[i]) : 0;
    ...
}
Fix:
for(int i = 0; i < len; i++) {
    int temp1 = (len1-1-i >= 0) ? Integer.valueOf(num1[len1-1-i]+"") : 0;
    int temp2 = (len2-1-i >= 0) ? Integer.valueOf(num2[len2-1-i]+"") : 0;
    ...
}

Solution 1:

public class Solution {
    static public String multiply(String num1, String num2) {
        if (num1 == null || num2 == null)
            throw new IllegalArgumentException("The passed in arguments is illegal!");
        char[] num2_array = num2.toCharArray(); 
        String ret = "0";
        for (int i = num2_array.length - 1; i >= 0; i--) {
            String temp = multiple(num1, num2_array[i]);
            int trail_zero = 0;
            while (trail_zero < num2_array.length-1-i) {
                temp += '0';
                trail_zero++;
            }
            ret = add(temp, ret);
        }
        while (ret.length() > 1 && ret.charAt(0) == '0')
            ret = ret.substring(1, ret.length());
        return ret;
    }
    
    static private String multiple(String num_str, Character n) {
        int carry = 0;
        char[] num = num_str.toCharArray();
        StringBuffer ret = new StringBuffer();
        for (int i = num.length - 1; i >= 0; i--) {
            int temp = Integer.valueOf(num[i]+"") * Integer.valueOf(n+"") + carry;
            int cur_digit = temp % 10;
            carry = temp / 10;
            ret.insert(0, cur_digit);
        }
        if (carry != 0)
            ret.insert(0, carry);
        return ret.toString();
    }
    
    static private String add(String num1_str, String num2_str) {
        char[] num1 = num1_str.toCharArray();
        char[] num2 = num2_str.toCharArray();
        int len1 = num1.length;
        int len2 = num2.length;
        
        int carry = 0;
        int len = Math.max(num1.length, num2.length);
        StringBuffer ret = new StringBuffer();
        for(int i = 0; i < len; i++) {
            int temp1 = (len1-1-i >= 0) ? Integer.valueOf(num1[len1-1-i]+"") : 0;
            int temp2 = (len2-1-i >= 0) ? Integer.valueOf(num2[len2-1-i]+"") : 0;
            int temp = temp1 + temp2 + carry;
            int cur_digit = temp % 10;
            carry = temp / 10;
            ret.insert(0, cur_digit);
        }
        if (carry != 0)
            ret.insert(0, carry);
        return ret.toString();
    }
}

Improvement analysis:

Apparently, although the above solutio is clear, but it is really hard to implement.
We could a use more concise solution for this problem.

When we multiply two numbers, we should keep following truth in mind.
Suppose: num1, num2, result.
result = num1 * num2
------------------------------------------------------------------
Truth 1: 
The result's length must no greater than num1.length+num2.length
------------------------------------------------------------------
Truth 2:
Each digit's weight were actually decided through 
digit[i+j] += (num1[i] * num2[j]) + carry;
Note the i,j (it free us from the previous solution at give us the underlying principle)
num1: 11
num2: 22
    11
    22
----------
    22
   22
 Note the underlying reaon!
------------------------------------------------------------------

Once we have above truth in mind, it is really easy to solve this problem. But You shoul note some implement skils.
Skill 1: the high digit is start from low index(0), and we need to start from low digit, how could we do it!!!
Reverse the string!!!
num1 = new StringBuffer(num1).reverse().toString();
num2 = new StringBuffer(num2).reverse().toString();

Skill 2: how to map digit value in character form into integer.
digit[i+j] += (num1.charAt(i) - '0') * (num2.charAt(j) - '0');

Skill 3: add carry for each digit (note the last digit has no carry)
for (int i = 0; i < digit.length; i++) {
    int mod = digit[i] % 10; //cause last digit has no carry.
    int carry = digit[i] / 10;
    if (i+1 < digit.length)
        digit[i+1] += carry; //Truth 1
    ret.insert(0, mod);
}

Skill 4: since we caculate from the last digit, how could we guarantee the right order?
ret.insert(0, mod);

Solution 2:

public class Solution {
    static public String multiply(String num1, String num2) {
        if (num1 == null || num2 == null)
            throw new IllegalArgumentException("The passed in arguments is illegal!");
        num1 = new StringBuffer(num1).reverse().toString();
        num2 = new StringBuffer(num2).reverse().toString();
        int[] digit = new int[num1.length() + num2.length()];
        StringBuffer ret = new StringBuffer();
        for (int i = 0; i < num1.length(); i++) {
            for (int j = 0; j < num2.length(); j++) {
                digit[i+j] += (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
            }
        }
        for (int i = 0; i < digit.length; i++) {
            int mod = digit[i] % 10;
            int carry = digit[i] / 10;
            if (i+1 < digit.length)
                digit[i+1] += carry;
            ret.insert(0, mod);
        }
        while (ret.length() > 1 && ret.charAt(0) == '0')
            ret.deleteCharAt(0);
        return ret.toString();
    }
}

 

转载于:https://www.cnblogs.com/airwindow/p/4779824.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值