您的位置:首页 > 编程语言 > C#

Leetcode-43-Multiply Strings C#

2015-11-02 10:36 721 查看
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的全部位数都对结果有影响。(要理解这句话,可以
4000
拿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开始全部位数都对结果有影响。

综合上面两条,可以将所有的情况规约成第二层循环的代码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  leetcode 算法 C# string