Extend through 3At index 1, 3 is positive: current_max=max(3,2*3)=6 and current_min=min(3,2*3)=3.
20scan31-2243-14
processedPrefix[0..1]endingRangemax [2,3]; min [3]arithmeticmax(3,6)=6; min(3,6)=3; best=6
Swap before multiplying by -2Because -2 is negative, swap the prior extremes: max source becomes 3 and min source becomes 6.
2031scan-2243-14
processedPrefix[0..2]actionswap current_max=6 and current_min=3sourcesAfterSwapmax source 3; min source 6
Update both products at -2After the swap, max(-2,3*-2=-6)=-2 and min(-2,6*-2=-12)=-12; best remains 6.
2031scan-2243-14
processedPrefix[0..2]endingRangemax [-2]; min [2,3,-2]arithmeticmax(-2,-6)=-2; min(-2,-12)=-12; best=6
Process positive 4At index 3, max(4,-2*4=-8)=4 restarts, while min(4,-12*4=-48)=-48 extends the negative product.
2031-22scan43-14
processedPrefix[0..3]endingRangemax [4]; min [2,3,-2,4]arithmeticmax(4,-8)=4; min(4,-48)=-48; best=6
Swap before multiplying by -1Because -1 is negative, the prior minimum -48 becomes the source for current_max and prior maximum 4 becomes the source for current_min.
2031-2243scan-14
processedPrefix[0..4]actionswap current_max=4 and current_min=-48sourcesAfterSwapmax source -48; min source 4
Turn the minimum into the maximummax(-1,-48*-1=48)=48 and min(-1,4*-1=-4)=-4; best becomes 48.
2031-2243scan-14
processedPrefix[0..4]endingRangemax [2,3,-2,4,-1]; min [4,-1]arithmeticmax(-1,48)=48; min(-1,-4)=-4; best=48result48 from [2,3,-2,4,-1]
Recognize it
Use paired extremes for contiguous products when negative values can reverse ordering and zero or a single value may force a restart.
Keep true
After each index, current_max and current_min are respectively the largest and smallest products of nonempty subarrays ending exactly there, and best is the largest current_max seen in the processed prefix.
Reuse it
When a transition is not monotone, retain every extreme that can become optimal after the next operation; sign-changing multiplication is the canonical max/min pair example.
Read it this way: At index 0, the only ending product is 2, so current_max=current_min=best=2. Step through the frames to watch the state change. The last frame shows the answer or the stopping condition.
Pattern: DP with current maximum and minimum.
Simple idea: A negative value can turn the smallest negative product into the largest
positive product. Keep both extremes. Swap them before multiplying by a negative value.
def max_product_subarray(nums: list[int]) -> int: current_max = current_min = best = nums[0] for num in nums[1:]: if num < 0: current_max, current_min = current_min, current_max current_max = max(num, current_max * num) current_min = min(num, current_min * num) best = max(best, current_max) return best