Leetcode-43-Multiply Strings C#

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


题意:给两个字符串,返回这两个字符串代表的数字的乘积字符串。

解法1:把字符串转换成数字,做乘法,再将结果转化回字符串,这种方法肯定是不行的,因为上限问题。所以需要自己实现一个乘法算法。乘法都学过,12345*54321,12345分别去乘以1,2,3,4,5得到5个数字,然后错位排列,想加得到结果。所以解题的思路就是用字符串来代替中间结果,先得到5个字符串,然后再错位想加。n位数与一个小于10的数相乘,写起来很简单,同样的n个个位数相加也不难实现。总体来讲,思路简单,写起来麻烦而已。

代码实现:

 public class Solution
    {
        public string Multiply(string num1, string num2)
        {
            if (num1 == "0" || num2 == "0")//有一个字符串为“0”就可以直接得到结果。
                return "0";
            string[] ret = new string[num2.Length];//字符串数组,存放num1与num2的每一位相乘的结果。
            for (int i = 0; i < num2.Length; i++)//num1与num2的倒数第i位相乘,得到的结果存到ret[i]中。
            {
                for (int j = 0; j < i; j++)//为了方便最后的想加计算,与第i位相乘的结果最后要补i个“0”。
                {
                    ret[i] =ret[i]+"0";
                }
                int carry = 0;//进位数。
                int tmp = 0;
                for (int k = 0; k < num1.Length; k++)
                {
                    tmp = (num1[num1.Length-k-1] -'0')*(num2[num2.Length-i-1]-'0')+carry;
                    ret[i] = (tmp % 10).ToString()+ret[i];
                    carry = tmp / 10;
                }
                ret[i] = carry == 0 ? ret[i] : carry.ToString() + ret[i];//最后要判断是否有进位。
            }
            return sum(ret);
        }

        public string sum(string[] str)
        {
            string ret = "";
            int len = str[str.Length - 1].Length;
            int carry = 0;
            for (int i = 0; i < len; i++)//将所有字符串到倒数第i位加起来
            {
                int tmp =0;
                for (int j = 0; j < str.Length; j++)//如果字符串没有倒数第i位,则跳过。
                {
                    if (i < str[j].Length)
                        tmp += str[j][str[j].Length - i - 1] - '0';
                }
                ret = ((tmp + carry) % 10).ToString() + ret;
                carry = (tmp + carry) / 10;
            }
            return carry == 0 ? ret : carry.ToString() + ret;
        }
    }

解法2:上面的解法比较好理解,但是效率不高,时间复杂度上且不说,需要记录若干字符串。我们来看一个简单的例子,12345*6789,一眼看过我们就可以得出结论,结果的个位数是5,为什么呢?因为只有5*9的结果会影响到个位,那么十位数字是几 呢,同样的道理,我们来看那几位的乘积会影响到十位数字。为了方便观察,我们把这两个数字的手动乘法过程写出来,如下:

  

个位上的5,我们已经分析过了,影响它的只有5*9,同时进位4,会影响到十位数字;

十位数字是0,影响到这一位的结果是4*9+5*8+4 = 80,所以得到十位数字是0,且进位8,会影响到百位数字;

百位数字是2,影响到这一位的结果是3*9+4*8+5*7+8 = 102,所以得到百位数字是2,且进位10,影响到千位数字;

其他位数不再做分析。

由上面的分析可以看出来,影响每一位数字的最终结果,都可以分解成几对小于等于9的数字乘积的和与下一位的进位和来表示。

说的简单一点,12345是一个5位数,6789是一个4位数,我们在手动运算时,撇开所有的进位操作不算,一共进行了5*4次乘法,这20次乘法得到的20个结果会影响最终结果的位数是不同的,正如上面分析的,5*9这一对的乘积结果只会影响个位数字,而3*9这一对会影响到百位数字。如果我们按照从低到高,分别找到影响每一位的所有乘积对,并将上一位的进位考虑进去,就可以依次的得到每一位的数字。

代码实现:

 public class Solution
    {
        public static string Multiply(string num1, string num2)
        {
            if (num1 == "0" || num2 == "0")//有一个字符串为“0”就可以直接得到结果。
                return "0";
            int m = num1.Length - 1, n = num2.Length - 1, carry = 0;
            StringBuilder product = new StringBuilder();//记录最终结果的逆序。
            for (int i = 0; i <= m + n || carry != 0; ++i)//m+1位数与n+1位数相乘最终结果最少有m+n+1位数,如果m+n+1位计                                                          //算完后carry不等于0,说明有m+n+2位。
            {
                for (int j = Math.Max(0, i - n); j <= Math.Min(i, m); ++j)//此段代码解释见后文。
                    carry += (num1[m - j] - '0') * (num2[n - i + j] - '0');
                product.Append((carry % 10).ToString());
                carry /= 10;
            }
            char[] ret = product.ToString().ToCharArray();//反转string
            Array.Reverse(ret);
            return new string(ret);
        }
    }
上面代码的内层循环比较难理解,这里着重解释一下:

在计算第i位时(i指的时倒数,从0开始算),我们需要找到num1的第a位,与num2的第b位,满足条件a+b = i,要找到这个条件的num1的起始位数,就可以确认下num2的起始位数。

当i小于等于n时说明,在计算0到n位,这时num1的个位数字对结果的第i位是有影响的,所以num1的开始位置是个位;此时如果i小于m说明了,num1的0到i位对结果的第i位也有影响;此时如果i大于m说明了,num1的全部位数都对结果有影响。(要理解这句话,可以拿123*456789为例子)

当i大于n说明,在计算n+1到m+n+1位,这时候num1的个位数字对结果的第i位就没有影响了,因为num1的个位能影响到的最大位数为第n位。此时num1中对结果有影响的开始位为i-n位,那么最高位是哪一位呢?跟上面情况相同,如果i小于m说明了,num1的i-n到i位对结果的第i位也有影响;此时如果i大于m说明了,num1的从i-n开始全部位数都对结果有影响。

综合上面两条,可以将所有的情况规约成第二层循环的代码。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值