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

LeetCode 1712: Ways to Split Array Into Three Subarrays

·2 分鐘· ·
LeetCode Medium Binary-Search Prefix-Sum Two-Pointers
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: medium 第一次嘗試:2026-01-22

  • 提交紀錄:TLE → WA(01-22)→ TLE → AC → TLE → AC → AC(bisect 版,01-24)

解題思路
#

切兩刀,把陣列分成 left、mid、right,要滿足 sum(left) <= sum(mid) <= sum(right)。數字都非負。

用前綴和 P[j] = nums[0] + ... + nums[j]。固定第一刀在 i(left = nums[0..i]),第二刀在 j(mid = nums[i+1..j]):

sum(left) <= sum(mid)   ⟺  P[i] <= P[j] - P[i]         ⟺  P[j] >= 2 * P[i]
sum(mid)  <= sum(right) ⟺  P[j] - P[i] <= P[-1] - P[j]  ⟺  P[j] <= (P[-1] + P[i]) / 2

數字非負,所以 P 是遞增的,合法的 j 是一段連續的區間,可以用 bisect 找左右邊界。另外 j 的範圍是 [i + 1, n - 2],因為三段都不能是空的。

我的嘗試
#

第一天:滑動窗口的思路不對
#

一開始想用一個窗口同時推三段,邏輯沒有對應到「固定第一刀、數第二刀的合法範圍」,TLE 和 WA。

第二天:兩個指針,但其中一個會往回走
#

改成固定 i,用 j_low、j_high 兩個指針找第二刀的範圍:

j_low = min(i + 1, j_low)          # 應該是 max

i 往右時,兩個邊界都只會往右移,所以指針應該只往前。寫成 min 會把 j_low 拉回 i + 1,每一輪都從頭掃,變成 O(n^2),TLE。改成 max 就 AC 了。

中間有一次 TLE 是不小心交回了 min 的舊版。

正確解法
#

解法一:兩次 bisect
#

import bisect
from itertools import accumulate

class Solution:
    def waysToSplit(self, nums: List[int]) -> int:
        P = list(accumulate(nums))
        n, total = len(nums), P[-1]
        ans = 0
        for i in range(n - 2):
            lo = bisect.bisect_left(P, 2 * P[i], i + 1, n - 1)
            hi = bisect.bisect_right(P, (total + P[i]) // 2, i + 1, n - 1)
            if hi > lo:
                ans += hi - lo
        return ans % (10 ** 9 + 7)

bisect 的 lo、hi 參數把搜尋範圍限制在 [i + 1, n - 1),剛好是第二刀的合法位置。

P[j] <= (total + P[i]) / 2 寫成整數的 // 2 也對:P[j] 是整數,小於等於某個數就等於小於等於它的 floor。

解法二:兩個只往前的指針
#

class Solution:
    def waysToSplit(self, nums: List[int]) -> int:
        n = len(nums)
        prefix = [0] * (n + 1)
        for i in range(n):
            prefix[i + 1] = prefix[i] + nums[i]

        ans = 0
        j_low = j_high = 1
        for i in range(n - 2):
            j_low = max(j_low, i + 1)
            while j_low < n - 1 and prefix[j_low + 1] < 2 * prefix[i + 1]:
                j_low += 1
            j_high = max(j_high, j_low)
            while j_high < n - 1 and 2 * prefix[j_high + 1] <= prefix[n] + prefix[i + 1]:
                j_high += 1
            ans += j_high - j_low
        return ans % (10 ** 9 + 7)

複雜度
#

解法時間
兩次 bisectO(n log n)
兩個單調指針O(n)

空間都是 O(n)。

要特別避免的錯誤
#

  • 單調指針寫成 min,讓它往回走。
  • 第二刀的範圍沒有限制在 [i + 1, n - 2]:三段都不能是空的。
  • 合法範圍是空的時候(hi <= lo)要跳過,不能把負數加進答案。

面試口說整理
#

我先建前綴和,固定第一刀 i,把兩個不等式改寫成第二刀前綴和的上下界:P[j] >= 2 P[i] 和 P[j] <= (total + P[i]) / 2。數字非負所以前綴和遞增,合法的第二刀是一段連續區間,用兩次 bisect 找邊界,並限制 j 在 [i+1, n-2]。也可以用兩個只往右移的指針做到 O(n)。

相關文章

LeetCode 209: Minimum Size Subarray Sum
·1 分鐘
LeetCode Daily Medium Array Sliding-Window Two-Pointers Prefix-Sum Binary-Search
滑動視窗與雙指針,最小化子陣列長度(總和 ≥ target)。
LeetCode 825: Friends Of Appropriate Ages
·2 分鐘
LeetCode Medium Two-Pointers Sorting Binary-Search Array
LeetCode 825 解題筆記:排序後,每個人能送出邀請的對象是年齡落在 (0.5 × age + 7, age] 的一段區間,用兩個同向指針維護區間再扣掉自己
LeetCode 3413: Maximum Coins From K Consecutive Bags
·2 分鐘
LeetCode Medium Sliding-Window Greedy Sorting Prefix-Sum Binary-Search Array
LeetCode 3413 解題筆記:最佳的 k 個袋子,一定有一端對齊某段的端點;分別算「左端對齊段的起點」和「右端對齊段的終點」(鏡像後重用同一個函式)
LeetCode 3814: Maximum Capacity Within Budget
·1 分鐘
LeetCode Medium Binary-Search Sorting Prefix-Sum
LeetCode 3814 解題筆記:依成本排序加上前綴最大容量,每台機器用 bisect 找能搭配的最貴機器
LeetCode 3709: Design Exam Scores Tracker
·1 分鐘
LeetCode Medium Binary-Search Prefix-Sum Design
LeetCode 3709 解題筆記:時間遞增,存前綴和,查詢用兩次 bisect;mid 一定要加括號
LeetCode 611: Valid Triangle Number
·1 分鐘
LeetCode Medium Array Two-Pointers Binary-Search Greedy
LeetCode 611 解題筆記:排序後固定最長邊 k,左右指針找兩短邊:和夠大時中間的都可以,一次加 j - i 個