1567. Maximum Length of Subarray With Positive Product
Problem Description
You are given an array of integers nums
. Your task is to find the maximum length of a subarray where the product of all its elements is positive.
A subarray is defined as a consecutive sequence of zero or more values taken from the original array. For example, if nums = [1, -2, 3, -4]
, then [1]
, [-2, 3]
, and [3, -4]
are all valid subarrays.
The product of a subarray is positive when:
- All elements in the subarray are positive, OR
- The subarray contains an even number of negative elements (and no zeros)
You need to return the maximum length among all possible subarrays that have a positive product.
For example:
- If
nums = [1, -2, -3, 4]
, the subarray[-2, -3, 4]
has length 3 and product 24 (positive), while[1, -2, -3]
has length 3 and product 6 (positive). The maximum length would be 4 for the entire array[1, -2, -3, 4]
which has product 24. - If
nums = [0, 1, -2, -3, -4]
, the subarray[-2, -3, -4]
has length 3 and product -24 (negative), but[-2, -3]
has length 2 and product 6 (positive). The maximum length of a positive product subarray would be 3.
Note that if the array contains a zero, it breaks the product calculation, so subarrays cannot span across zeros.
Intuition
The key insight is that the sign of a product depends on the count of negative numbers. An even count of negatives gives a positive product, while an odd count gives a negative product. Zeros reset everything since any product involving zero becomes zero.
As we traverse the array, at each position we need to track two things:
- The longest subarray ending at the current position with a positive product
- The longest subarray ending at the current position with a negative product
Why track both? Because when we encounter a negative number, these two lengths swap roles - a positive product becomes negative when multiplied by a negative number, and vice versa.
Consider what happens at each element:
- Positive number: A positive product stays positive (just extend it by 1), and a negative product stays negative (also extend by 1)
- Negative number: A positive product becomes negative, and a negative product becomes positive (they swap)
- Zero: Both lengths reset to 0 since we can't continue any subarray through a zero
This leads us to use dynamic programming with two arrays:
f[i]
: length of longest positive-product subarray ending at indexi
g[i]
: length of longest negative-product subarray ending at indexi
The magic happens when we hit a negative number - if we had a negative-product subarray of length k
ending at position i-1
, multiplying by the current negative number gives us a positive-product subarray of length k+1
at position i
. Similarly, a positive-product subarray becomes negative.
By tracking the maximum value of f[i]
throughout our traversal, we find the longest subarray with a positive product.
Learn more about Greedy and Dynamic Programming patterns.
Solution Approach
We implement the solution using dynamic programming with two arrays f
and g
of length n
:
f[i]
represents the length of the longest subarray ending atnums[i]
with a positive productg[i]
represents the length of the longest subarray ending atnums[i]
with a negative product
Initialization:
- If
nums[0] > 0
: Setf[0] = 1
(we have a positive subarray of length 1), andg[0] = 0
(no negative subarray) - If
nums[0] < 0
: Setf[0] = 0
(no positive subarray), andg[0] = 1
(we have a negative subarray of length 1) - If
nums[0] = 0
: Bothf[0] = 0
andg[0] = 0
(zero breaks any product) - Initialize answer
ans = f[0]
State Transition:
For each index i
from 1 to n-1
, we update based on the value of nums[i]
:
-
When
nums[i] > 0
:f[i] = f[i-1] + 1
(positive stays positive, just extend by 1)g[i] = 0 if g[i-1] == 0 else g[i-1] + 1
(if there was no negative subarray before, there still isn't; otherwise extend it)
-
When
nums[i] < 0
:f[i] = 0 if g[i-1] == 0 else g[i-1] + 1
(negative × negative = positive, so we get positive from previous negative)g[i] = f[i-1] + 1
(positive × negative = negative, so previous positive becomes negative)
-
When
nums[i] = 0
:- Both
f[i] = 0
andg[i] = 0
(zero resets everything)
- Both
After processing each element, we update ans = max(ans, f[i])
to track the maximum length of a positive product subarray seen so far.
Example walkthrough with nums = [1, -2, -3, 4]
:
i=0
:nums[0]=1>0
, sof[0]=1
,g[0]=0
,ans=1
i=1
:nums[1]=-2<0
, sof[1]=0
,g[1]=f[0]+1=2
,ans=1
i=2
:nums[2]=-3<0
, sof[2]=g[1]+1=3
,g[2]=f[1]+1=1
,ans=3
i=3
:nums[3]=4>0
, sof[3]=f[2]+1=4
,g[3]=g[2]+1=2
,ans=4
The final answer is 4, representing the entire array [1, -2, -3, 4]
which has a positive product.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [2, -1, -3, 0, 5, -2]
.
We'll track two arrays:
f[i]
: length of longest positive-product subarray ending at indexi
g[i]
: length of longest negative-product subarray ending at indexi
Initial Setup:
- Arrays:
f = [0, 0, 0, 0, 0, 0]
andg = [0, 0, 0, 0, 0, 0]
- We'll update these as we process each element
Step-by-step processing:
i = 0, nums[0] = 2 (positive):
- Since it's positive:
f[0] = 1
(single positive element) - No negative product:
g[0] = 0
- Update
ans = 1
- State:
f = [1, 0, 0, 0, 0, 0]
,g = [0, 0, 0, 0, 0, 0]
i = 1, nums[1] = -1 (negative):
- Previous positive becomes negative:
g[1] = f[0] + 1 = 1 + 1 = 2
- No previous negative to make positive:
f[1] = 0
(sinceg[0] = 0
) ans
remains 1- State:
f = [1, 0, 0, 0, 0, 0]
,g = [0, 2, 0, 0, 0, 0]
i = 2, nums[2] = -3 (negative):
- Previous negative becomes positive:
f[2] = g[1] + 1 = 2 + 1 = 3
- Previous positive becomes negative:
g[2] = f[1] + 1 = 0 + 1 = 1
- Update
ans = max(1, 3) = 3
- State:
f = [1, 0, 3, 0, 0, 0]
,g = [0, 2, 1, 0, 0, 0]
i = 3, nums[3] = 0 (zero):
- Zero resets everything:
f[3] = 0
,g[3] = 0
ans
remains 3- State:
f = [1, 0, 3, 0, 0, 0]
,g = [0, 2, 1, 0, 0, 0]
i = 4, nums[4] = 5 (positive):
- New positive number after zero:
f[4] = f[3] + 1 = 0 + 1 = 1
- No negative product:
g[4] = 0
(sinceg[3] = 0
) ans
remains 3- State:
f = [1, 0, 3, 0, 1, 0]
,g = [0, 2, 1, 0, 0, 0]
i = 5, nums[5] = -2 (negative):
- Previous positive becomes negative:
g[5] = f[4] + 1 = 1 + 1 = 2
- No previous negative to make positive:
f[5] = 0
(sinceg[4] = 0
) ans
remains 3- State:
f = [1, 0, 3, 0, 1, 0]
,g = [0, 2, 1, 0, 0, 2]
Final Result: The maximum length is 3, corresponding to the subarray [2, -1, -3]
which has product 6 (positive).
The key observations:
- When we hit
-1
at index 1, the positive subarray[2]
became negative[2, -1]
- When we hit
-3
at index 2, the negative subarray[2, -1]
became positive[2, -1, -3]
with length 3 - The zero at index 3 prevented any subarray from spanning across it
- After the zero, we started fresh but couldn't achieve a longer positive product subarray
Solution Implementation
1class Solution:
2 def getMaxLen(self, nums: List[int]) -> int:
3 """
4 Find the maximum length of a subarray where the product of all elements is positive.
5
6 Args:
7 nums: List of integers
8
9 Returns:
10 Maximum length of subarray with positive product
11 """
12 n = len(nums)
13
14 # Dynamic programming arrays
15 # positive_length[i]: max length of subarray ending at index i with positive product
16 # negative_length[i]: max length of subarray ending at index i with negative product
17 positive_length = [0] * n
18 negative_length = [0] * n
19
20 # Initialize base case for first element
21 positive_length[0] = 1 if nums[0] > 0 else 0
22 negative_length[0] = 1 if nums[0] < 0 else 0
23
24 # Track the maximum length found so far
25 max_length = positive_length[0]
26
27 # Process each element starting from index 1
28 for i in range(1, n):
29 current_num = nums[i]
30
31 if current_num > 0:
32 # Positive number: extends positive subarray by 1
33 positive_length[i] = positive_length[i - 1] + 1
34
35 # Only extends negative subarray if one exists
36 negative_length[i] = negative_length[i - 1] + 1 if negative_length[i - 1] > 0 else 0
37
38 elif current_num < 0:
39 # Negative number: flips the sign of products
40 # Previous negative becomes positive (if exists)
41 positive_length[i] = negative_length[i - 1] + 1 if negative_length[i - 1] > 0 else 0
42
43 # Previous positive becomes negative
44 negative_length[i] = positive_length[i - 1] + 1
45
46 else: # current_num == 0
47 # Zero breaks the subarray, reset both lengths
48 positive_length[i] = 0
49 negative_length[i] = 0
50
51 # Update maximum length with current positive subarray length
52 max_length = max(max_length, positive_length[i])
53
54 return max_length
55
1class Solution {
2 public int getMaxLen(int[] nums) {
3 int n = nums.length;
4
5 // dp[i] represents the maximum length of subarray ending at index i with positive product
6 int[] positiveLength = new int[n];
7 // dp[i] represents the maximum length of subarray ending at index i with negative product
8 int[] negativeLength = new int[n];
9
10 // Initialize base case for first element
11 positiveLength[0] = nums[0] > 0 ? 1 : 0;
12 negativeLength[0] = nums[0] < 0 ? 1 : 0;
13
14 // Track the maximum length of positive product subarray
15 int maxLength = positiveLength[0];
16
17 // Dynamic programming: process each element from index 1
18 for (int i = 1; i < n; ++i) {
19 if (nums[i] > 0) {
20 // Current element is positive
21 // Positive length extends from previous positive length
22 positiveLength[i] = positiveLength[i - 1] + 1;
23 // Negative length extends from previous negative length (if exists)
24 negativeLength[i] = negativeLength[i - 1] > 0 ? negativeLength[i - 1] + 1 : 0;
25 } else if (nums[i] < 0) {
26 // Current element is negative
27 // Positive product comes from previous negative (negative * negative = positive)
28 positiveLength[i] = negativeLength[i - 1] > 0 ? negativeLength[i - 1] + 1 : 0;
29 // Negative product comes from previous positive (positive * negative = negative)
30 negativeLength[i] = positiveLength[i - 1] + 1;
31 } else {
32 // Current element is 0, reset both lengths to 0
33 positiveLength[i] = 0;
34 negativeLength[i] = 0;
35 }
36
37 // Update maximum length
38 maxLength = Math.max(maxLength, positiveLength[i]);
39 }
40
41 return maxLength;
42 }
43}
44
1class Solution {
2public:
3 int getMaxLen(vector<int>& nums) {
4 int n = nums.size();
5
6 // Dynamic programming arrays:
7 // maxPositive[i]: max length of subarray ending at index i with positive product
8 // maxNegative[i]: max length of subarray ending at index i with negative product
9 vector<int> maxPositive(n, 0);
10 vector<int> maxNegative(n, 0);
11
12 // Base case: initialize for the first element
13 maxPositive[0] = nums[0] > 0 ? 1 : 0;
14 maxNegative[0] = nums[0] < 0 ? 1 : 0;
15
16 // Track the maximum length found so far
17 int maxLength = maxPositive[0];
18
19 // Process each element starting from index 1
20 for (int i = 1; i < n; ++i) {
21 if (nums[i] > 0) {
22 // Positive number: extends positive product subarray by 1
23 maxPositive[i] = maxPositive[i - 1] + 1;
24
25 // Extends negative product subarray only if previous negative exists
26 maxNegative[i] = maxNegative[i - 1] > 0 ? maxNegative[i - 1] + 1 : 0;
27 }
28 else if (nums[i] < 0) {
29 // Negative number: flips the sign of products
30 // Previous negative becomes positive
31 maxPositive[i] = maxNegative[i - 1] > 0 ? maxNegative[i - 1] + 1 : 0;
32
33 // Previous positive becomes negative
34 maxNegative[i] = maxPositive[i - 1] + 1;
35 }
36 // If nums[i] == 0, both arrays remain 0 (default value)
37
38 // Update the maximum length with current positive product length
39 maxLength = max(maxLength, maxPositive[i]);
40 }
41
42 return maxLength;
43 }
44};
45
1/**
2 * Finds the maximum length of a subarray with positive product
3 * @param nums - Array of integers (positive, negative, or zero)
4 * @returns Maximum length of subarray with positive product
5 */
6function getMaxLen(nums: number[]): number {
7 const arrayLength: number = nums.length;
8
9 // Dynamic programming arrays
10 // positiveLength[i]: max length of subarray ending at index i with positive product
11 const positiveLength: number[] = Array(arrayLength).fill(0);
12 // negativeLength[i]: max length of subarray ending at index i with negative product
13 const negativeLength: number[] = Array(arrayLength).fill(0);
14
15 // Initialize base case for first element
16 if (nums[0] > 0) {
17 positiveLength[0] = 1;
18 }
19 if (nums[0] < 0) {
20 negativeLength[0] = 1;
21 }
22
23 // Track the maximum length found so far
24 let maxLength: number = positiveLength[0];
25
26 // Build up the solution using dynamic programming
27 for (let i = 1; i < arrayLength; i++) {
28 if (nums[i] > 0) {
29 // When current number is positive:
30 // - Positive subarray extends by 1
31 // - Negative subarray extends by 1 only if previous negative exists
32 positiveLength[i] = positiveLength[i - 1] + 1;
33 negativeLength[i] = negativeLength[i - 1] > 0 ? negativeLength[i - 1] + 1 : 0;
34 } else if (nums[i] < 0) {
35 // When current number is negative:
36 // - Positive comes from previous negative (negative × negative = positive)
37 // - Negative comes from previous positive (positive × negative = negative)
38 positiveLength[i] = negativeLength[i - 1] > 0 ? negativeLength[i - 1] + 1 : 0;
39 negativeLength[i] = positiveLength[i - 1] + 1;
40 }
41 // If nums[i] === 0, both arrays remain 0 at index i (already filled with 0)
42
43 // Update maximum length
44 maxLength = Math.max(maxLength, positiveLength[i]);
45 }
46
47 return maxLength;
48}
49
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. This is because the algorithm iterates through the array exactly once with a single for loop from index 1 to n-1, and within each iteration, it performs only constant-time operations (comparisons, assignments, and basic arithmetic operations).
The space complexity is O(n)
, where n
is the length of the array nums
. This is due to the allocation of two auxiliary arrays f
and g
, each of size n
, to store the dynamic programming states. The array f[i]
stores the maximum length of a subarray ending at index i
with a positive product, while g[i]
stores the maximum length of a subarray ending at index i
with a negative product.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Not Handling Zero Elements Properly
A common mistake is forgetting that zeros completely break the product chain. When encountering a zero, both positive and negative subarray lengths must reset to 0, not just continue from previous values.
Incorrect approach:
if current_num == 0: positive_length[i] = positive_length[i - 1] # Wrong! Should be 0 negative_length[i] = negative_length[i - 1] # Wrong! Should be 0
Correct approach:
if current_num == 0: positive_length[i] = 0 negative_length[i] = 0
2. Incorrect Initialization of Negative Length
When a negative number is encountered and there's no existing negative subarray (negative_length[i-1] == 0), developers might incorrectly set negative_length[i] = 0 instead of properly starting a new negative subarray.
Incorrect approach:
elif current_num < 0: positive_length[i] = negative_length[i - 1] + 1 if negative_length[i - 1] > 0 else 0 negative_length[i] = positive_length[i - 1] # Wrong! Forgets to add 1
Correct approach:
elif current_num < 0: positive_length[i] = negative_length[i - 1] + 1 if negative_length[i - 1] > 0 else 0 negative_length[i] = positive_length[i - 1] + 1 # Always extends by 1
3. Space Optimization Pitfall
When optimizing space by using variables instead of arrays, a critical error occurs if you update both variables using the old values without storing them first.
Incorrect space-optimized approach:
for i in range(1, n):
if nums[i] < 0:
positive = negative + 1 if negative > 0 else 0
negative = positive + 1 # Wrong! Uses updated positive value
Correct space-optimized approach:
for i in range(1, n):
if nums[i] < 0:
temp = positive # Store old value first
positive = negative + 1 if negative > 0 else 0
negative = temp + 1 # Use the stored old value
4. Edge Case: Single Element Array
Failing to properly handle arrays with only one element, especially when that element is negative or zero.
Solution: The initialization step should correctly handle this:
- If nums = [-5], the answer should be 0 (no positive product subarray)
- If nums = [5], the answer should be 1
- If nums = [0], the answer should be 0
The provided code handles this correctly through proper initialization, but it's easy to overlook in custom implementations.
Which two pointer techniques do you use to check if a string is a palindrome?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!