给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
输入: num1 = "2", num2 = "3"
输出: "6"
输入: num1 = "123", num2 = "456"
输出: "56088"
https://leetcode-cn.com/problems/multiply-strings
一开始的想法是, 先把字符串转成数字相乘, 在把结果转回成字符串, 哈哈哈, 果然测试用例已经替你考虑好了, 有几个巨长无比的字符串, 保证会让你溢出的.
那就模拟一下正常的乘法怎么计算的, 比如说123*456,可以通过乘法分配率拆成,123*6+123*50+123*400, 然后得到结果在相加就是56088. 这样的方法可以完成,但是写起来感觉比较麻烦, 得一直处理进位, 而且相加也得考虑进位.
要是能不处理进位就好了, 但是不处理进位是不可能的, 那要是能统一处理进位也可以接受, 于是就变成了这样, 每个数字和每个数字单独相乘, 然后放到数组中的特定位置, 相乘结束后, 在统一的处理一次进位, 这样代码的可读性就好多了.
大体思路就是这样图, 剩下的就是具体的操作了, 看注释吧.
class Solution {
func multiply(_ num1: String, _ num2: String) -> String {
// 如果有一个为0, 那么结果肯定就是0
if Int(num1) == 0 || Int(num2) == 0 {
return "0"
}
// 把字符串转成数组, 同时把低位放到了前面, 比如"123" -> [3,2,1]
let num1Array = num1.reversed().map { (item) -> Int in
return Int(String(item))!
}
let num2Array = num2.reversed().map { (item) -> Int in
return Int(String(item))!
}
// 2数相乘,最终结果肯定小于位数和,所以结果放到result中肯定没有问题,不会溢出
// 证明: 已知 10^m-1<= num1 <10^m, 10^n-1<= num2 <10^n,
// 那么 10^(m+n-2)<= num1*num2 <10^(m+n).
// 举例, 100*100<999*999<1000*1000
var result = [Int].init(repeating: 0, count: num1Array.count+num2Array.count)
var i = 0
var j = 0
// 让num1中的每一位和num2中的每一位相乘, 结果放到i+j中,result中的每一个元素可能会超过10,下面会处理
for firstNum in num1Array {
j = 0
for secondNum in num2Array {
let temp = firstNum * secondNum
result[i+j] += temp
j += 1
}
i += 1
}
// 处理result中大于10的情况, 如果大于10, 本位取余数, 同时向高位进位
// 比如个位上是88,那么个位上变成8,十位在原来的基础上+8,
for (i,_) in result.enumerated() {
if result[i]>=10 {
result[i+1] += result[i]/10
result[i] %= 10
}
}
// 此时result中个位在最前面,反转之后,把result转成字符串, 同时去掉最高位的若干个0,
result = result.reversed()
var characterArray = result.flatMap { String($0) }
for item in characterArray {
if Int(item.description)! == 0 {
characterArray.removeFirst()
} else {
break
}
}
let resultStr = String(characterArray)
return resultStr
}
}
看了其他效率更好的算法, 确实有优化的空间, 但是可读性就降低了很多, 更高效的算法是在2层循环中同时处理好result, 这里是先计算好result, 等2层循环结束, 在单独处理, 分开处理, 我觉得可读性更好, 更适合学习理解, 所以就没有在优化了.