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个个位数相加也不难实现。总体来讲,思路简单,写起来麻烦而已。
代码实现:
解法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开始全部位数都对结果有影响。
综合上面两条,可以将所有的情况规约成第二层循环的代码。
题意:给两个字符串,返回这两个字符串代表的数字的乘积字符串。
解法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开始全部位数都对结果有影响。
综合上面两条,可以将所有的情况规约成第二层循环的代码。
相关文章推荐
- Android之获取手机上的图片和视频缩略图thumbnails
- android string.xml文件中的整型和string型代替
- Android java 与 javascript互访(相互调用)的方法例子
- android上改变listView的选中颜色
- String.intern
- c#调用COM组件
- 动易2006序列号破解算法公布
- Prototype源码浅析 String部分(二)
- C#实现把指定数据写入串口
- C#动态创建button的方法
- C#中抽象方法与虚拟方法的区别
- c#中虚函数的相关使用方法
- C#使用加边法计算行列式的值
- C#实现多线程的同步方法实例分析
- C#中尾递归的使用、优化及编译器优化
- C#实现子窗体与父窗体通信方法实例总结
- C#通用邮件发送类分享
- Ruby中的String对象学习笔记
- C#中this的用法集锦
- C#.NET获取拨号连接的宽带连接方法