โ† 2026-09-23 ๐Ÿ“‚ All Days 2026-10-09 โ†’

๐Ÿ”ฌ Saturday Deep Dive: Maximum Product Subarray โ€” ไน˜็งฏๅญๆ•ฐ็ป„็š„ๅŒ่ฝจ DP (15 min read)

๐Ÿ“Š NeetCode: 106/150 ยท SysDesign: 60/40 ยท Behavioral: 60/40 ยท Frontend: 37/50 ยท AI: 30/30

๐Ÿ”ฅ Day 137!


Overview / ๆฆ‚่ฟฐ

ไธญๆ–‡๏ผš

Maximum Product Subarray๏ผˆLeetCode #152๏ผ‰ๆ˜ฏไธ€้“็œ‹ไผผ็ฎ€ๅ•ใ€ๅฎžๅˆ™ๆš—่—ๆ€ๆœบ็š„ DP ้ข˜ใ€‚ๅฆ‚ๆžœไฝ ็”จ Kadane ็ฎ—ๆณ•็š„ๆ€่ทฏๅŽปๅšไน˜ๆณ•็‰ˆๆœฌ๏ผŒไฝ ไผšๅœจ่ดŸๆ•ฐไธŠๆ‘”่ทŸๅคดใ€‚ๆ ธๅฟƒ้—ฎ้ข˜ๆ˜ฏ๏ผš่ดŸๆ•ฐไน˜ไปฅ่ดŸๆ•ฐไผšๅ˜ๆˆๆญฃๆ•ฐโ€”โ€”่ฟ™ๆ„ๅ‘ณ็€"ๅฝ“ๅ‰ๆœ€ๅฐๅ€ผ"้šๆ—ถๅฏ่ƒฝ็ฟป่บซๆˆไธบ"ๅฝ“ๅ‰ๆœ€ๅคงๅ€ผ"ใ€‚

ๆœฌๆœŸๆทฑๅบฆ่งฃๆž๏ผš

  1. ไธบไป€ไนˆไน˜็งฏ DP ๅฟ…้กปๅŒๆ—ถ่ฟฝ่ธช max ๅ’Œ min
  2. ๅฎŒๆ•ด็š„็Šถๆ€่ฝฌ็งปๆŽจๅฏผ่ฟ‡็จ‹
  3. ไปŽๆšดๅŠ› O(nยฒ) ๅˆฐๆœ€ไผ˜ O(n) ็š„่ฟ›ๅŒ–่ทฏๅพ„
  4. ่ดŸๆ•ฐใ€้›ถใ€ๅ…จ่ดŸๆ•ฐๆ•ฐ็ป„็ญ‰ 5 ็ง่พน็•Œๆƒ…ๅ†ต
  5. ่ฟ™ไธช"ๅŒ่ฝจ่ฟฝ่ธช"ๆŠ€ๅทงๅœจๅ…ถไป–้ข˜็›ฎไธŠ็š„ๅปถไผธ

English๏ผš

Maximum Product Subarray (#152) is deceptively tricky. If you naively apply Kadane's algorithm to multiplication, negatives will bite you. The core insight: a negative ร— a negative = a positive, meaning today's minimum product could become tomorrow's maximum.

This deep dive covers:

  1. Why you must track both max and min simultaneously
  2. Full derivation of the state transition
  3. Evolution from brute force O(nยฒ) to optimal O(n)
  4. 5 edge cases that trip up candidates
  5. Where this "dual-track DP" pattern appears in other problems

๐Ÿ”— LeetCode: https://leetcode.com/problems/maximum-product-subarray/

๐Ÿ“น NeetCode: https://neetcode.io/problems/maximum-product-subarray


Part 1: Theory / ็†่ฎบๅŸบ็ก€ (5 min)

ไธบไป€ไนˆๅŠ ๆณ• DP ไธ่ƒฝ็›ดๆŽฅ็”จไบŽไน˜ๆณ•๏ผŸ/ Why Kadane's Doesn't Directly Transfer

ไธญๆ–‡๏ผš

ๅ…ˆๅ›žๅฟ† Kadane ็ฎ—ๆณ•๏ผˆๆœ€ๅคงๅญๆ•ฐ็ป„ๅ’Œ๏ผ‰็š„ๆ ธๅฟƒๆ€ๆƒณ๏ผš

max_ending_here = max(num, max_ending_here + num)

้€ป่พ‘ๅพˆ็ฎ€ๅ•๏ผš่ฆไนˆไปŽๅฝ“ๅ‰ๆ•ฐ้‡ๆ–ฐๅผ€ๅง‹๏ผŒ่ฆไนˆๆŠŠๅฝ“ๅ‰ๆ•ฐๅŠ ๅˆฐไน‹ๅ‰็š„ๆœ€ๅคงๅญๆ•ฐ็ป„ไธŠใ€‚

ไฝ†ไน˜ๆณ•ๅฎŒๅ…จไธๅŒใ€‚่€ƒ่™‘ๆ•ฐ็ป„ [-2, -3, 7]๏ผš

  • Kadane ๆ€็ปด๏ผš-2 ไธบ่ดŸ๏ผŒไธขๆމ๏ผ›-3 ไธบ่ดŸ๏ผŒไธขๆމ๏ผ›็ญ”ๆกˆๆ˜ฏ 7
  • ๆญฃ็กฎ็ญ”ๆกˆ๏ผš(-2) ร— (-3) ร— 7 = 42

ไธบไป€ไนˆ๏ผŸๅ› ไธบไธคไธช่ดŸๆ•ฐ็›ธไน˜ๅ˜ๆญฃๆ•ฐ๏ผŒ่€Œไธ”ไน˜็งฏ็š„็ปๅฏนๅ€ผๅฏไปฅๅพˆๅคงใ€‚

ๅ…ณ้”ฎๆŽจ่ฎบ๏ผšๅœจไน˜ๆณ• DP ไธญ๏ผŒๆˆ‘ไปฌๅฟ…้กปๅŒๆ—ถ็ปดๆŠคไธคไธช็Šถๆ€โ€”โ€”ไปฅ i ็ป“ๅฐพ็š„ๅญๆ•ฐ็ป„็š„ๆœ€ๅคงไน˜็งฏ๏ผˆmax_prod๏ผ‰ๅ’Œๆœ€ๅฐไน˜็งฏ๏ผˆmin_prod๏ผ‰ใ€‚

English๏ผš

Recall Kadane's core idea (maximum subarray sum):

max_ending_here = max(num, max_ending_here + num)

Simple logic: either restart from current element, or extend the previous best subarray.

With multiplication, consider [-2, -3, 7]:

  • Kadane thinking: -2 is negative, discard; -3 is negative, discard; answer = 7
  • Correct answer: (-2) ร— (-3) ร— 7 = 42

Why? Two negatives multiply to a positive, potentially huge. The key corollary: in product DP, we must maintain both max_prod and min_prod ending at position i.


็Šถๆ€ๅฎšไน‰ / State Definition

ไธญๆ–‡๏ผš

max_prod[i] = ไปฅ nums[i] ็ป“ๅฐพ็š„ๆ‰€ๆœ‰ๅญๆ•ฐ็ป„ไธญไน˜็งฏๆœ€ๅคง็š„ๅ€ผ
min_prod[i] = ไปฅ nums[i] ็ป“ๅฐพ็š„ๆ‰€ๆœ‰ๅญๆ•ฐ็ป„ไธญไน˜็งฏๆœ€ๅฐ็š„ๅ€ผ

็Šถๆ€่ฝฌ็งปๆ–น็จ‹๏ผˆ3 ็งๅ€™้€‰๏ผ‰๏ผš

ไปฅ nums[i] ็ป“ๅฐพ็š„ๅญๆ•ฐ็ป„๏ผŒๅชๆœ‰ 3 ็งๆƒ…ๅ†ต๏ผš
  1. ไป…ๅŒ…ๅซ nums[i] ๆœฌ่บซ
  2. nums[i] ร— max_prod[i-1]  ๏ผˆๆ‰ฉๅฑ•ไน‹ๅ‰็š„ๆœ€ๅคงไน˜็งฏ๏ผ‰
  3. nums[i] ร— min_prod[i-1]  ๏ผˆๆ‰ฉๅฑ•ไน‹ๅ‰็š„ๆœ€ๅฐไน˜็งฏ๏ผ‰

ๅฝ“ nums[i] > 0 ๆ—ถ๏ผŒๆƒ…ๅ†ต2้€šๅธธไบง็”Ÿๆ–ฐๆœ€ๅคงๅ€ผ
ๅฝ“ nums[i] < 0 ๆ—ถ๏ผŒๆƒ…ๅ†ต3ๅฏ่ƒฝ็ฟป่ฝฌๆˆๆ–ฐๆœ€ๅคงๅ€ผ๏ผˆ่ดŸ ร— ่ดŸ = ๆญฃ๏ผ๏ผ‰
ๅฝ“ nums[i] = 0 ๆ—ถ๏ผŒ3็งๅ€™้€‰้ƒฝๅฝ’้›ถ๏ผŒ้‡ๆ–ฐๅผ€ๅง‹

ๅ› ๆญค๏ผš

max_prod[i] = max(nums[i], nums[i] ร— max_prod[i-1], nums[i] ร— min_prod[i-1])
min_prod[i] = min(nums[i], nums[i] ร— max_prod[i-1], nums[i] ร— min_prod[i-1])

English๏ผš

max_prod[i] = maximum product of any subarray ending at nums[i]
min_prod[i] = minimum product of any subarray ending at nums[i]

State transition โ€” 3 candidates at each position:

Subarrays ending at nums[i] fall into exactly 3 cases:
  1. Just nums[i] alone
  2. nums[i] ร— max_prod[i-1]  (extend previous max)
  3. nums[i] ร— min_prod[i-1]  (extend previous min)

When nums[i] > 0: case 2 likely gives the new max
When nums[i] < 0: case 3 might flip to new max (negative ร— negative = positive!)
When nums[i] = 0: all candidates are 0; reset

Therefore:

max_prod[i] = max(nums[i], nums[i] ร— max_prod[i-1], nums[i] ร— min_prod[i-1])
min_prod[i] = min(nums[i], nums[i] ร— max_prod[i-1], nums[i] ร— min_prod[i-1])

็›ด่ง‰ๅ›พ / Intuition Diagram

Input: [-2, 3, -4] Position: 0 1 2 Value: -2 3 -4 max_prod: -2 3 -4 ร— -2 = 8 โ† ่ดŸ ร— ่ดŸ ็ฟป่ฝฌ๏ผ min_prod: -2 -2ร—3=-6 -6ร—(-4)=-24 (new min, useless for now) Global result: max(-2, 3, 8) = 8 โœ“ The "min track" (-6 at pos 1) became critical at pos 2: -6 ร— (-4) = +24... wait let me retrace. Correct trace for [-2, 3, -4]: i=0: max=-2, min=-2 i=1: candidates=(3, 3ร—(-2)=-6, 3ร—(-2)=-6) โ†’ max=3, min=-6 i=2: candidates=(-4, 3ร—(-4)=-12, -6ร—(-4)=24) โ†’ max=24, min=-12 Answer: max(-2, 3, 24) = 24 โœ“ The min_prod of -6 at i=1 became the hero at i=2!

Part 2: Step-by-Step Implementation / ไธ€ๆญฅไธ€ๆญฅๅฎž็Žฐ (8 min)

Approach 1: Naive Brute Force โ€” O(nยฒ)

ไธญๆ–‡๏ผšๆšดๅŠ›่งฃๆณ•๏ผšๆžšไธพๆ‰€ๆœ‰ๅญๆ•ฐ็ป„ใ€‚้ข่ฏ•ๆ—ถๅฑ•็คบ"ๆˆ‘็Ÿฅ้“ๆ›ดไผ˜่งฃ๏ผŒๅ…ˆ็ป™ๅŸบๅ‡†"ใ€‚

English: Brute force โ€” enumerate all subarrays. Show this first to establish baseline.

def maxProduct_brute(nums: list[int]) -> int:
    """
    Brute force: try every possible subarray [i..j]
    Time: O(n^2), Space: O(1)
    """
    n = len(nums)
    result = nums[0]
    
    for i in range(n):
        product = 1
        for j in range(i, n):
            product *= nums[j]              # extend subarray [i..j]
            result = max(result, product)   # update global max
    
    return result

# Test: [2, 3, -2, 4] โ†’ 6 (subarray [2,3])
print(maxProduct_brute([2, 3, -2, 4]))   # 6 โœ“
print(maxProduct_brute([-2, 0, -1]))      # 0 โœ“
print(maxProduct_brute([-2, 3, -4]))      # 24 โœ“

Approach 2: Optimal Dual-Track DP โ€” O(n) time, O(1) space

def maxProduct(nums: list[int]) -> int:
    """
    Dual-track DP: track both max and min product ending at current position.
    
    Key insight: when nums[i] is negative, multiply by min_prod (which is
    also negative) gives a large positive โ€” the new max.
    
    Time: O(n), Space: O(1)
    """
    if not nums:
        return 0
    
    # Initialize: both max and min start as nums[0]
    max_prod = nums[0]   # max product of subarray ending here
    min_prod = nums[0]   # min product of subarray ending here  
    result = nums[0]     # global maximum found so far
    
    for i in range(1, len(nums)):
        n = nums[i]
        
        # --- CRITICAL: compute candidates BEFORE overwriting max_prod/min_prod ---
        # Three possible products for subarrays ending at index i:
        candidate1 = n                  # start fresh: just [nums[i]]
        candidate2 = n * max_prod       # extend previous max subarray
        candidate3 = n * min_prod       # extend previous min subarray
        
        # New max is the largest of the three
        max_prod = max(candidate1, candidate2, candidate3)
        # New min is the smallest (track for future negative multiplications)
        min_prod = min(candidate1, candidate2, candidate3)
        
        # Update global result
        result = max(result, max_prod)
    
    return result


# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# Trace through [2, 3, -2, 4]:
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# Start:  max_prod=2, min_prod=2, result=2
#
# i=1, n=3:
#   candidates = (3, 3*2=6, 3*2=6)
#   max_prod=6, min_prod=3, result=6
#
# i=2, n=-2:
#   candidates = (-2, -2*6=-12, -2*3=-6)
#   max_prod=-2, min_prod=-12, result=6
#
# i=3, n=4:
#   candidates = (4, 4*(-2)=-8, 4*(-12)=-48)
#   max_prod=4, min_prod=-48, result=6
#
# Final: 6 โœ“ (subarray [2,3])

Full Walkthrough with State Table / ๅฎŒๆ•ด็Šถๆ€่กจ

def maxProduct_verbose(nums: list[int]) -> int:
    """Same algorithm with step-by-step logging for understanding."""
    max_prod = min_prod = result = nums[0]
    print(f"Init: max={max_prod}, min={min_prod}, result={result}")
    
    for i in range(1, len(nums)):
        n = nums[i]
        # Must save before overwriting!
        prev_max, prev_min = max_prod, min_prod
        
        max_prod = max(n, n * prev_max, n * prev_min)
        min_prod = min(n, n * prev_max, n * prev_min)
        result = max(result, max_prod)
        
        print(f"i={i}, n={n}: "
              f"candidates=({n}, {n*prev_max}, {n*prev_min}) "
              f"โ†’ max={max_prod}, min={min_prod}, result={result}")
    
    return result

# Output for [-2, 3, -4]:
# Init: max=-2, min=-2, result=-2
# i=1, n=3: candidates=(3, -6, -6) โ†’ max=3, min=-6, result=3
# i=2, n=-4: candidates=(-4, -12, 24) โ†’ max=24, min=-12, result=24
# Answer: 24 โœ“

One-Liner Trick (Pythonic) / ไธ€่กŒๆŠ€ๅทง

ไธญๆ–‡๏ผšๅฏไปฅ็”จ min() ๅ’Œ max() ็ป„ๅˆๆˆๆ›ด็ฎ€ๆด็š„ๅ†™ๆณ•๏ผš

English: A compact version using tuple unpacking:

def maxProduct_compact(nums: list[int]) -> int:
    max_p = min_p = result = nums[0]
    for n in nums[1:]:
        # Python evaluates the ENTIRE right-hand tuple BEFORE any assignment,
        # so both max() and min() see the original (old) max_p and min_p. โœ“
        max_p, min_p = (
            max(n, n * max_p, n * min_p),
            min(n, n * max_p, n * min_p)
        )
        result = max(result, max_p)
    return result
    
# ๐Ÿ’ก Python tuple assignment is safe here: the RHS is fully evaluated first.
# max_p and min_p on the right still refer to the OLD values.
# If you prefer explicit clarity, save old values first:
# Safer (more readable) pattern:

def maxProduct_safe(nums: list[int]) -> int:
    max_p = min_p = result = nums[0]
    for n in nums[1:]:
        # Save originals explicitly
        old_max, old_min = max_p, min_p
        max_p = max(n, n * old_max, n * old_min)
        min_p = min(n, n * old_max, n * old_min)
        result = max(result, max_p)
    return result

Cross-Check: Diagram โ†” Code Trace / ไบคๅ‰้ชŒ่ฏ

Pick input: [-2, 3, -4]

State Diagram: โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Index: 0 1 2 Value: -2 3 -4 โ†“ โ†“ โ†“ max_p -2 โ†’ max(3,-6,-6)=3 โ†’ max(-4,-12,24)=24 โ† HERO min_p -2 โ†’ min(3,-6,-6)=-6 โ†’ min(-4,-12,24)=-12 result -2 3 24 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ†‘ min=-6 here โ†‘ minร—n=-6ร—-4=24 โ† min_prod rescued us!

Code trace (using maxProduct_safe):

  • Init: max_p=-2, min_p=-2, result=-2
  • n=3: old_max=-2, old_min=-2 โ†’ max_p=max(3,-6,-6)=3, min_p=min(3,-6,-6)=-6, result=3
  • n=-4: old_max=3, old_min=-6 โ†’ max_p=max(-4,-12,24)=24, min_p=min(-4,-12,24)=-12, result=24
  • Return 24 โœ“

Diagram matches code exactly. The min_prod=-6 tracked at i=1 is used at i=2 (-6 ร— -4 = 24) โ€” matching the diagram's "HERO" path. โœ“


Part 3: Edge Cases & Gotchas / ่พน็•Œๆƒ…ๅ†ต (2 min)

ไธญๆ–‡๏ผš5 ไธชๅฟ…้กปๆต‹่ฏ•็š„่พน็•Œๆƒ…ๅ†ต๏ผš

English: 5 edge cases every interviewer expects you to handle:

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Case 1: Single element (including negative)
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
assert maxProduct_safe([-3]) == -3          # Can't avoid it; must take it

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Case 2: Contains zero โ†’ resets the product chain
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
assert maxProduct_safe([-2, 0, -1]) == 0    # Zero breaks both sides
# Why: [-2,0] = 0, [0,-1] = 0, [-2,0,-1] = 0 โ†’ best is 0

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Case 3: All negative (even count) โ†’ product is positive
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
assert maxProduct_safe([-2, -3, -4]) == 12  # (-2)ร—(-3)ร—(-4)... nope
# Wait: best subarray is [-2,-3]โ†’6 or [-3,-4]โ†’12 โ†’ answer is 12 โœ“

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Case 4: All negative (odd count) โ†’ forced to leave one out
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
assert maxProduct_safe([-2, -3, -4, -5]) == 120  # All four: (-2)ร—(-3)ร—(-4)ร—(-5) = 120
# Even count of negatives โ†’ full array product is positive. Answer: 120

assert maxProduct_safe([-2, -3, -4]) == 12  # Odd count โ†’ skip one end
# Best: skip -2 โ†’ (-3)ร—(-4)=12, or skip -4 โ†’ (-2)ร—(-3)=6 โ†’ answer 12

# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Case 5: Multiple zeros splitting the array
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
assert maxProduct_safe([0, 2, 0, -5, 0]) == 2  # Each segment independent

้ข่ฏ•ๆณจๆ„็‚น / Interview Gotchas:

ๆƒ…ๅ†ตๅŽŸๅ› ๅค„็†ๆ–นๅผ
้‡ๅˆฐ 0ไน˜็งฏๅฝ’้›ถ๏ผŒไน‹ๅŽ็š„ๅญๆ•ฐ็ป„็‹ฌ็ซ‹่ฎก็ฎ—ๅ€™้€‰1๏ผˆไป…nums[i]๏ผ‰=0๏ผŒ่‡ช็„ถ้‡็ฝฎ
ๅ…จ่ดŸๅถๆ•ฐไธชๅ…จ้ƒจ็›ธไน˜ๆœ€ไผ˜็ฎ—ๆณ•่‡ชๅŠจๅค„็†
ๅ…จ่ดŸๅฅ‡ๆ•ฐไธชๅŽปๆމ็ปๅฏนๅ€ผๆœ€ๅฐ็š„็ซฏ็‚น็ฎ—ๆณ•่‡ชๅŠจๅค„็†
ๅ•ๅ…ƒ็ด ๅฟ…้กป่ฟ”ๅ›ž่ฏฅๅ…ƒ็ด ๏ผˆๅณไฝฟไธบ่ดŸ๏ผ‰ๅˆๅง‹ๅŒ–ไธบnums[0]
ๅ€™้€‰่ฎก็ฎ—้กบๅบๅฟ…้กป็”จไธŠไธ€่ฝฎ็š„max/minๅ…ˆไฟๅญ˜ old_max, old_min

Part 4: Real-World Application / ๅฎž้™…ๅบ”็”จ (2 min)

ไธญๆ–‡๏ผš

่ฟ™้“้ข˜็š„ๆ ธๅฟƒๆŠ€ๅทงโ€”โ€”ๅŒๆ—ถ่ฟฝ่ธชๆœ€ๅคงๅ€ผๅ’Œๆœ€ๅฐๅ€ผโ€”โ€”ๅœจ็”Ÿไบง็Žฏๅขƒไธญๆœ‰็œŸๅฎž็š„็ฑปๆฏ”ๅœบๆ™ฏ๏ผš

1. ้‡‘่ž้ฃŽ้™ฉ่ฎก็ฎ—๏ผš

ไธ€็ณปๅˆ—ไน˜ๆณ•ๆ”ถ็›Š็އ๏ผˆๅฆ‚ +10%, -20%, +30%๏ผ‰ไธญ๏ผŒๆ‰พๆœ€ไผ˜ๆŒไป“็ช—ๅฃใ€‚่ดŸๆ”ถ็›Šๅฏ่ƒฝ"ๆ•‘ๆดป"ๅฆไธ€ๆฎต่ดŸๆ”ถ็›ŠๆœŸ๏ผˆ็ฉบๅคดไป“ไฝ๏ผ‰ใ€‚่ฟฝ่ธชๆœ€ๅคงๆฝœๅœจๆ”ถ็›Šๅ’Œๆœ€ๅคงๆฝœๅœจๆŸๅคฑๅŒๆ ท้‡่ฆใ€‚

2. ๅฏนๆ•ฐ่ฝฌๆข โ†’ ๅŠ ๆณ•๏ผš

ๅœจๅฎž้™…็ณป็ปŸไธญ๏ผŒ่ฟž็ปญไน˜็งฏๅธธ็”จๅฏนๆ•ฐ่ฝฌๅŒ–ไธบๅŠ ๆณ•๏ผˆ้ฟๅ…ไธ‹ๆบข/ไธŠๆบข๏ผ‰๏ผš

import math
# Max product of subarray โ‰ก max sum of log(|nums[i]|)
# ไฝ†่ฆๅค„็†่ดŸๆ•ฐ็š„็ฌฆๅท้—ฎ้ข˜ โ†’ ้œ€่ฆ็‰นๆฎŠๆ ‡่ฎฐ

3. NLP/ML ๅบๅˆ—่ฏ„ๅˆ†๏ผš

่ฏญ่จ€ๆจกๅž‹่ฎก็ฎ—ๅฅๅญๆฆ‚็އๆ˜ฏๅ„่ฏๆฆ‚็އ็š„่ฟžไน˜็งฏใ€‚ๆ‰พ"ๆœ€้ซ˜็ฝฎไฟกๅบฆๅญๅบๅˆ—"ๆœฌ่ดจไธŠๆ˜ฏๅ˜็ง็š„ Maximum Product Subarrayใ€‚

English:

The dual-track technique appears in real systems:

1. Financial returns: A series of multiplicative returns (+10%, -20%, +30%) โ€” find the optimal holding window. Short positions mean negative returns can compound positively.

2. Log-space arithmetic: Production ML systems compute log-probabilities (sums) instead of raw products to avoid underflow. But the sign-tracking problem remains.

3. Sequence scoring: Finding the highest-confidence span in an NLP model's token probability sequence is exactly this pattern.

The abstract pattern (generalize this!):

"When the 'worst' state can become the 'best' state due to the nature of the operation, you must track both extremes."

This appears in: Maximum Product Subarray, Maximum Absolute Sum of Subarray (#1749), products with complex numbers, etc.


Part 5: Interview Simulation / ้ข่ฏ•ๆจกๆ‹Ÿ (3 min)

ๅœบๆ™ฏ๏ผšไฝ ๅœจ Meta E5 ้ข่ฏ•๏ผŒ้ข˜็›ฎๆ˜ฏ #152


Q1: ้ข่ฏ•ๅฎ˜้—ฎ๏ผšไธบไป€ไนˆ่ฆ่ฟฝ่ธช min_prod๏ผŸไธๅฐฑๆ˜ฏ่ฟฝ่ธชๆœ€ๅคง็š„ๅฐฑๅฅฝไบ†๏ผŸ

A: ไธๅคŸใ€‚ไน˜ๆณ•ๆœ‰"็ฟป่ฝฌ"ๆ•ˆๅบ”๏ผšๅฝ“ๅ‰็š„ๆœ€ๅฐไน˜็งฏ๏ผˆ่ดŸๆ•ฐ๏ผ‰ไน˜ไปฅไธ‹ไธ€ไธช่ดŸๆ•ฐ๏ผŒไผšๅ˜ๆˆๆœ€ๅคงไน˜็งฏใ€‚ๅฆ‚ๆžœๅช่ฟฝ่ธช max_prod๏ผŒไฝ ไผš้”™่ฟ‡่ฟ™ไธช็ฟป่ฝฌๆœบไผšใ€‚
ไพ‹ๅญ๏ผš[-5, -3] โ†’ ไป…่ฟฝ่ธช max ็š„่ฏ๏ผŒmax_prod[-5]=-5๏ผ›ๅˆฐ -3 ๆ—ถ๏ผŒๅ€™้€‰ๆ˜ฏ๏ผˆ-3, -3ร—(-5)=15, -3ร—(-5)=15๏ผ‰โ†’ max=15ใ€‚่ฟ™้‡Œๆฐๅฅฝ่ฟ˜ๆ˜ฏๅฏน็š„๏ผŒไฝ†ๅœจๆ›ดๅคๆ‚็š„ๅบๅˆ—ไธญไฝ ้œ€่ฆ min ๆฅ้ฟๅ…้”™่ฟ‡ๆทฑๅฑ‚็ฟป่ฝฌใ€‚

Q2: ่ฟ™ไธช็ฎ—ๆณ•ๅค„็†ๅ…จ้›ถๆ•ฐ็ป„ๆญฃ็กฎๅ—๏ผŸ

A: ๆ˜ฏ็š„ใ€‚ๅ…จ้›ถๆ•ฐ็ป„ [0, 0, 0]๏ผšๅˆๅง‹ๅŒ– result=0๏ผ›ๆฏๆญฅไธ‰ไธชๅ€™้€‰้ƒฝๆ˜ฏ 0๏ผ›ๆœ€็ปˆ่ฟ”ๅ›ž 0ใ€‚ๆญฃ็กฎใ€‚

Q3: ๅฆ‚ๆžœ้ข˜็›ฎๆ”นๆˆ"ๆœ€ๅฐไน˜็งฏๅญๆ•ฐ็ป„"ๆ€Žไนˆๆ”น๏ผŸ

A: ๅช้œ€ๆŠŠ result = max(result, max_prod) ๆ”นๆˆ result = min(result, min_prod)๏ผŒๅ…ถไฝ™้€ป่พ‘ๅฎŒๅ…จ็›ธๅŒใ€‚min_prod ๅทฒ็ปๅœจ่ฟฝ่ธชไบ†ใ€‚

Q4: ่ƒฝ็”จ Divide and Conquer ่งฃ่ฟ™้“้ข˜ๅ—๏ผŸๆ—ถ้—ดๅคๆ‚ๅบฆๅคšๅฐ‘๏ผŸ

A: ๅฏไปฅ๏ผŒ็ฑปไผผ Maximum Subarray ็š„ๅˆ†ๆฒป่งฃๆณ•ใ€‚ๆฏๆฌกไปŽไธญ็‚นๅˆ†ๅ‰ฒ๏ผŒ็ญ”ๆกˆๆ˜ฏไปฅไธ‹ไธ‰่€…ไน‹ไธ€๏ผšๅทฆๅŠ้ƒจๅˆ†็š„ๆœ€ๅคงไน˜็งฏใ€ๅณๅŠ้ƒจๅˆ†็š„ๆœ€ๅคงไน˜็งฏใ€ๆˆ–่ทจ่ถŠไธญ็‚น็š„ๆœ€ๅคงไน˜็งฏใ€‚่ทจ่ถŠไธญ็‚น็š„่ฎก็ฎ—้œ€่ฆไปŽไธญ็‚นๅ‘ไธคไพงๆ‰ฉๅฑ•๏ผŒไฝ†ไน˜ๆณ•็‰ˆๆœฌ้œ€่ฆๅŒๆ—ถ็ปดๆŠคๆœ€ๅคงๅ’Œๆœ€ๅฐ๏ผŒๅคๆ‚ๅบฆ O(n log n)ใ€‚ไธๅฆ‚็บฟๆ€ง DP๏ผŒ้€šๅธธๆฒกๅฟ…่ฆๆ๏ผŒไฝ†ไฝ“็Žฐไบ†ๅˆ†ๆฒปๆ€็ปดใ€‚

Q5: ๅฆ‚ๆžœๆ•ฐ็ป„้žๅธธๅคง๏ผˆ10ไบฟๅ…ƒ็ด ๏ผ‰๏ผŒๅฆ‚ไฝ•ๅนถ่กŒๅŒ–๏ผŸ

A: ่ฟ™ๆ˜ฏ็ณป็ปŸ่ฎพ่ฎกๅปถไผธใ€‚ๅฏไปฅๅˆ†ๅ—ๅนถ่กŒ๏ผŒๆฏๅ—่ฟ”ๅ›ž (max_prod, min_prod, prefix_max, prefix_min, suffix_max, suffix_min, total_product)๏ผŒ็„ถๅŽๅˆๅนถใ€‚็ฑปไผผ segment tree ็š„ๆ€่ทฏ๏ผŒ้€‚ๅˆ MapReduce ๆˆ– Sparkใ€‚ๅˆๅนถ้€ป่พ‘ๆ˜ฏ่ฟ™้“้ข˜็Šถๆ€่ฝฌ็งปๆ–น็จ‹็š„"ๅˆ†ๅธƒๅผ็‰ˆๆœฌ"ใ€‚

Summary / ๆ€ป็ป“

ไธญๆ–‡๏ผš

็ปดๅบฆ่ฆ็‚น
ๆ ธๅฟƒๆดžๅฏŸ่ดŸ ร— ่ดŸ = ๆญฃ๏ผŒๅฟ…้กปๅŒๆ—ถ่ฟฝ่ธช max ๅ’Œ min
็Šถๆ€max_prod[i], min_prod[i]๏ผŒ็ฉบ้—ดไผ˜ๅŒ–ไธบ O(1)
่ฝฌ็งปๆ–น็จ‹max(n, nร—prev_max, nร—prev_min)
้›ถ็š„ๅค„็†ๅ€™้€‰1๏ผˆไป…nๆœฌ่บซ๏ผ‰่‡ชๅŠจๅค„็†้‡็ฝฎ
ๆ—ถ้—ดๅคๆ‚ๅบฆO(n)
้ข่ฏ•ๆŠ€ๅทงๅ…ˆๅฑ•็คบๆšดๅŠ›่งฃ๏ผŒๅ†ๆŽจๅฏผๅŒ่ฝจ DP๏ผŒ็”จๅ…ทไฝ“ไพ‹ๅญ่ฏดๆ˜Žไธบไฝ•้œ€่ฆ min_prod

English:

DimensionKey Point
Core InsightNegative ร— negative = positive; track both max AND min
Statemax_prod[i], min_prod[i]; space-optimize to O(1)
Recurrencemax(n, nร—prev_max, nร—prev_min)
Zero handlingCandidate 1 (just n) auto-resets the chain
Time ComplexityO(n)
Interview tipShow brute force first, derive dual-track DP, use [-2,3,-4] to motivate why min_prod matters

ๅปถไผธ็ปƒไน  / Related Problems:

  • #53 Maximum Subarray (ๅŠ ๆณ•็‰ˆ๏ผŒKadane ็ปๅ…ธ)
  • #1749 Maximum Absolute Sum of Any Subarray (่ฟฝ่ธช็ปๅฏนๅ€ผ็š„ๆœ€ๅคงๅ’Œๆœ€ๅฐ)
  • #628 Maximum Product of Three Numbers (ไธ‰ๅ…ƒ็ด ๏ผŒๆŽ’ๅบ่ดชๅฟƒ)
  • #238 Product of Array Except Self (ๅ‰็ผ€/ๅŽ็ผ€ไน˜็งฏ)

byte-by-byte Day 137 โ€” Saturday Deep Dive

Generated: 2026-09-26