1. 程式人生 > 實用技巧 >LeetCode 43. 字串相乘 | Python

LeetCode 43. 字串相乘 | Python

43. 字串相乘


題目來源:力扣(LeetCode)https://leetcode-cn.com/problems/multiply-strings

題目


給定兩個以字串形式表示的非負整數 num1 和 num2,返回 num1 和 num2 的乘積,它們的乘積也表示為字串形式。

示例 1:

輸入: num1 = "2", num2 = "3"
輸出: "6"

示例 2:

輸入: num1 = "123", num2 = "456"
輸出: "56088"

說明:

  • num1 和 num2 的長度小於110。
  • num1 和 num2 只包含數字 0-9。
  • num1 和 num2 均不以零開頭,除非是數字 0 本身。
  • 不能使用任何標準庫的大數型別(比如 BigInteger)或直接將輸入轉換為整數來處理。

解題思路


思路:豎式運算

首先審題,題目要求的是乘積,那麼我們可以模擬豎式乘法來計算乘積。

這裡先要注意一種情況,當 num1 和 num2 任意一個為 0 時,直接返回 0。

如果 num1 和 num2 都不為 0,那麼我們可以遍歷 num2 每一位和 num1 進行相乘,然後將每次相乘的結果進行累加。(這其實就是我們豎式乘法運算的思想)如下圖示:

之前我們曾遇到過 415. 字串相加,我們在後續的累加的部分可以直接使用此題的思想。還有一個需要注意的,除了 num2 最低位與 num1 的運算除外,其他位與 num1 的乘積都應該補 0。

具體程式碼實現見【程式碼實現 # 豎式運算】

在上面的演算法中,需要對每步計算計算的字串進行相加的過程,多次對字串進行操作,會消耗效能。

在這裡,我們對豎式運算進行優化,我們採用陣列代替字串儲存結果,避免頻繁操作字串。

先看題目後面的說明中,第 2、3 條說明 num1 和 num2 不存在前導 0 的情況,並且 num1 和 num2 中每位數只包含 0 到 9 之間的數字。在這裡,我們根據這個來確定定義陣列的長度。

我們知道,(自然數中)只有 1 位的數字最小的是 0,最大為 9。兩位數最小的是 10,最大的是 99。我們可以發現,這裡是有規律的。假設 n 為數字位數,那麼當 n = 1 時,最小數字為 $0=10^{0}$,最大數字為 $9 = 10^{1}-1$;當 n = 2 時,最小數字為 $10 = 10^{2-1}$,最大數字為 $99 = 10^{2}-1$。也就說,當 n 取大於 0 的數(正整數)時,n 位數最小數為 $10^{n-1}$,最大數為 $10^{n} - 1$。

現在我們假設,N1、N2 分別為 num1 和 num2 的長度。這裡看 num1 和 num2 分別去最小值和最大值時可能的長度。

  • 當 $\rm{num1}$ 和 $\rm{num2}$ 取最小值時,也就是 $\rm{num1}= 10^{N1-1}, \rm{num2}= 10^{N2-1}$,那麼兩者的乘積 $\rm{num1} \times \rm{num2}= 10^{N1+N2-2}$,也就說此時乘積的長度為 $N1+N2-1$;
  • 當 $\rm{num1}$ 和 $\rm{num2}$ 取最大值時,也就是 $\rm{num1}= 10^{N1}-1, \rm{num2}= 10^{N2}-1$,那麼兩者的乘積 $\rm{num1} \times \rm{num2}= 10^{N1+N2} - 10{N1}-10{N2}+1$,這裡我們可以發現,兩者的乘積是介於 [$10^{N1+N2-1}$, $10^{N1+N2}$] 之間的。也就說長度為 $N1+N2$

在這裡,兩數相乘的最大長度為兩數長度之和。那麼定義陣列 ans 長度為 len(num1)+len(num2)。對於任意 $0\leq i <N1$ 和 $0\leq j < N2$,索引對應的數值乘積結果(最大兩位數,形如 'ab','0a' 的形式),結果第一位位於 ans[i+j] 中,第二位位於 ans[i+j+1] 中。可結合下圖理解:

具體程式碼見【程式碼實現 # 豎式運算(優化)】

程式碼實現


# 豎式運算
class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        if num1 == '0' or num2 == '0':
            return '0'

        ans = '0'
        N1, N2 = len(num1), len(num2)
        # 從後往前遍歷 num2,與 num1 相乘
        for i in range(N2-1, -1, -1):
            carry = 0
            k = int(num2[i])
            # 補充 0
            curr = ['0'] * (N2-i-1)
            for j in range(N1-1, -1, -1):
                product = int(num1[j]) * k + carry
                carry = product // 10
                curr.append(str(product%10))
            # 確認 carry 是否不為 0,不為 0 則補充在最高位
            if carry != 0:
                curr.append(str(carry))
            # 形成字串,放入下列求和方法中
            curr = ''.join(curr[::-1])
            ans = self.addStrings(ans, curr)
        return ans

    def addStrings(self, num1: str, num2: str) -> str:
        # 用以儲存計算結果
        ans = []
        # 定義指標分別指向 num1, num2 末尾
        p = len(num1) - 1
        q = len(num2) - 1
        # 儲存進位
        carry = 0

        # 模擬加法運算
        # 這裡將 carry 放到條件中,是考慮後續計算結束後,還有進位,也就是 carry 為 1 的情況
        while p >= 0 or q >= 0 or carry:
            # 由於有可能出現索引溢位的現象,
            # 當較短的字串索引溢位時,要在頭部新增 0,用以後續計算
            elem1 = int(num1[p]) if p >= 0 else 0
            elem2 = int(num2[q]) if q >= 0 else 0
            # 模擬計算,注意加上進位
            tmp = elem1 + elem2 + carry
            # 相加結果可能大於 10
            # 計算進位,並且就將結果模 10,餘數新增到 ans 頭部
            carry = tmp // 10
            # ans = str(tmp % 10) + ans
            ans.append(str(tmp % 10))
            # 往前繼續計算
            p -= 1
            q -= 1

        return ''.join(ans[::-1])

# 豎式運算(優化)
class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        if num1 == "0" or num2 == "0":
            return "0"

        N1, N2 = len(num1), len(num2)

        ans = [0] * (N1+N2)

        for i in range(N1-1, -1, -1):
            a = int(num1[i])
            for j in range(N2-1, -1, -1):
                b = int(num2[j])
                tmp  = ans[i+j+1] + a * b
                ans[i+j] += tmp // 10
                ans[i+j+1] = tmp % 10
        
        # 文章分析了取值為最小最大時,兩數乘積的長度為 N1 + N2 - 1 和 N1 + N2
        # 這裡注意陣列中首元素
        ans = ans[1:] if ans[0] == 0 else ans

        return ''.join(str(x) for x in ans)

實現結果


歡迎關注


公眾號 【書所集錄