↓快轉到主要內容
  1. LeetCode/

LeetCode 927: Three Equal Parts

·2 分鐘· ·
LeetCode Hard Math Array
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: hard 第一次嘗試:2025-07-16

  • 提交紀錄:18 次(WA / RE 很多次 → 2025-07-17 早上 AC → 重寫又錯 7 次 → AC)

解題思路
#

前導零不影響值,所以三段代表同一個二進位數 ⇔ 去掉前導零之後完全一樣。

  1. 三段的 1 一定一樣多:總數不能被 3 整除就不可能。
  2. 全部都是 0:任何切法都是 0,回傳 [0, n - 1]。
  3. 每段有 k = 1 的總數 / 3 個 1。找出第 1、k + 1、2k + 1 個 1 的位置 a、b、c,它們分別是三段去掉前導零後的開頭。
  4. 第三段一定延伸到陣列尾端,尾端的 0 都算它的,所以每段的長度(從第一個 1 算起)都是 L = n - c。
  5. 檢查 arr[a:a+L]、arr[b:b+L]、arr[c:] 是否一樣,而且第一段結束在第二段開始之前(a + L <= b)、第二段結束在第三段開始之前(b + L <= c)。
  6. 答案是 [a + L - 1, b + L]。

我的嘗試
#

這題我寫了 18 次。前 9 次是從右往左掃、邊掃邊用 2 ** (位置) 累加每段的值,邊界一直出問題:

  • 全部是 0 時沒處理:[0, 0, 0] 會直接 IndexError(res 是空的),或回傳 [-1, -1],答案是 [0, 2]。中間有一版甚至寫成 if arr == [0, 0, 0, 0, 0]: return [0, 4] 來過特定測資。
  • 第一次 AC 是把它改成 part_count == 0 時回傳 [0, n - 1]。

同一天早上又用「先取出第三段的樣板,再往左比對」重寫了一次,回傳的 index 加不加 1、樣板的位置一直弄錯,又錯了 7 次。例如 [1, 1, 1] 回傳了 [0, 1](答案是 [0, 2]),[1, 1, 1, 1, 1, 1] 回傳 [1, 3](答案是 [1, 4])。

上面的解法用三個位置 a、b、c 直接比對,不需要算值,也比較不會差一。

正確解法
#

class Solution:
    def threeEqualParts(self, arr: List[int]) -> List[int]:
        n = len(arr)
        ones = sum(arr)
        if ones == 0:
            return [0, n - 1]                      # 全部是 0
        if ones % 3:
            return [-1, -1]
        k = ones // 3

        starts = []                                # 第 1、k+1、2k+1 個 1 的位置
        seen = 0
        for i, x in enumerate(arr):
            if x:
                if seen % k == 0:
                    starts.append(i)
                seen += 1
        a, b, c = starts
        L = n - c                                  # 第三段延伸到尾端,決定長度

        if a + L <= b and b + L <= c and arr[a:a + L] == arr[b:b + L] == arr[c:]:
            return [a + L - 1, b + L]
        return [-1, -1]

複雜度
#

時間 O(n),空間 O(n)(切片比較;也可以用三個指針逐位比較做到 O(1))。

要特別避免的錯誤
#

  • 全部是 0 的特例。
  • 用 2 ** 位置 算值:陣列長度到 3 × 10^4,數字非常大,而且容易差一。
  • 回傳的 j 是第三段的開頭,不是第二段的結尾。

面試口說整理
#

前導零不影響值,所以三段去掉前導零後要完全一樣。我先數 1:不能被 3 整除就不行,全部是 0 就隨便切。接著找出每段第一個 1 的位置,第三段一定延伸到尾端,所以它的長度決定每段要比對多長,比對三段相同而且不重疊,就能算出切點。時間 O(n)。

相關文章

LeetCode 952: Largest Component Size by Common Factor
·2 分鐘
LeetCode Hard Math Number-Theory Union-Find Hash-Table Array
LeetCode 952 解題筆記:有共同因數 ⇔ 有共同的質因數;把每個數分解質因數,同一個質因數的 index 用 Union-Find 合併,不用兩兩算 GCD
LeetCode 3518: Smallest Palindromic Rearrangement II
·2 分鐘
LeetCode Hard Math Combinatorics Counting Hash-Table String
LeetCode 3518 解題筆記:回文由左半邊決定,變成「多重集合的第 k 個排列」;逐位試每個字元,用排列數判斷第 k 個落在哪個分支,排列數可以 O(1) 更新
LeetCode 1814: Count Nice Pairs in an Array
·1 分鐘
LeetCode Medium Array Hash-Table Math Counting
LeetCode 1814 解題筆記:把條件移項成 nums[i] - rev(nums[i]) == nums[j] - rev(nums[j]),按這個值分組數 pair,記得取模
LeetCode 313: Super Ugly Number
·1 分鐘
LeetCode Medium Array Math Dynamic-Programming
LeetCode 313 解題筆記:LC 264 的推廣,每個質數一個指針;用 heap 的話要避免同一個數被推進去很多次
LeetCode 396: Rotate Function
·1 分鐘
LeetCode Medium Array Math Dynamic-Programming
LeetCode 396 解題筆記:F(k) 和 F(k - 1) 只差一個公式:F(k) = F(k - 1) + sum - n × 被移到最前面的那個數
LeetCode 189: Rotate Array
·1 分鐘
LeetCode Medium Array Math Two-Pointers
LeetCode 189 解題筆記:先 k %= n,再用三次反轉:整個反轉、前 k 個反轉、後 n - k 個反轉;O(1) 額外空間