zoukankan      html  css  js  c++  java
  • [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();
        }
    }
  • 相关阅读:
    cad.net 仿lisp函数专篇
    操作篇 cad一个小技巧,通过块中块插入含有字段块,保证更新
    cad.net 外部参照功能和相对路径转换
    cad.net 动态块名 .IsDynamicBlock出错 eInvalidObjectId错误.
    cad.net 委托的学习
    cad.net 关于保存文件Database.SaveAs()出现"eFileAccessErr"错误的解决方法
    测试篇 c# winFrom Close报错 System.ObjectDisposedException:“无法访问已释放的对象。
    测试篇 c#枚举类型怎么用?
    cad.net 2008使用WPF(摘录山人)
    日志篇 随着win10更新...
  • 原文地址:https://www.cnblogs.com/airwindow/p/4779824.html
Copyright © 2011-2022 走看看