Maximum Length of Subarray With Positive Product
MedPrompt
Given an array of integers nums, find the maximum length of a subarray where the product of all its elements is positive.
A subarray of an array is a consecutive sequence of zero or more values taken out of that array.
Return the maximum length of a subarray with positive product.
Example 1:
Input: nums = [1,-2,-3,4]
Output: 4
Explanation: The array nums already has a positive product of 24.Example 2:
Input: nums = [0,1,-2,-3,-4]
Output: 3
Explanation: The longest subarray with positive product is [1,-2,-3] which has a product of 6.
Notice that we cannot include 0 in the subarray since that'll make the product 0 which is not positive.Example 3:
Input: nums = [-1,-2,-3,0,1]
Output: 2
Explanation: The longest subarray with positive product is [-1,-2] or [-2,-3].
Constraints:
1 <= nums.length <= 105-109 <= nums[i] <= 109
Approaches
2 approaches with complexity analysis and trade-offs.
The brute-force approach is the most straightforward way to solve the problem. It involves generating every possible contiguous subarray, checking if the product of its elements is positive, and keeping track of the maximum length found. To avoid potential integer overflows from calculating the actual product, we only need to track the sign of the product. A zero in a subarray makes its product zero (not positive), and an even number of negative elements results in a positive product.
Algorithm
- Initialize
maxLengthto 0. - Use a nested loop structure. The outer loop with index
idefines the start of a subarray, and the inner loop with indexjdefines the end. - For each subarray
nums[i...j], calculate the sign of its product. - To do this efficiently, for a fixed
i, asjincreases, we can maintain the sign of the product ofnums[i...j]in O(1) time. - Initialize a variable
productSign = 1before the inner loop. - Inside the inner loop (for
j):- If
nums[j]is 0, the product becomes 0. No subarray starting atiand includingjcan have a positive product. So, we break the inner loop. - If
nums[j]is negative, we flip the sign:productSign *= -1. - If
productSignis positive (i.e.,1), it means the subarraynums[i...j]has a positive product. We updatemaxLength = max(maxLength, j - i + 1).
- If
- After iterating through all possible subarrays, return
maxLength.
Walkthrough
We can implement this using two nested loops. The outer loop iterates through all possible starting points i of a subarray, and the inner loop iterates through all possible ending points j. For each subarray nums[i...j], we determine the sign of its product. Instead of re-computing the product for each subarray, we can maintain a running sign. When we extend the subarray from nums[i...j-1] to nums[i...j], we update the sign based on nums[j]. If nums[j] is positive, the sign remains unchanged. If nums[j] is negative, the sign flips. If nums[j] is zero, the product becomes zero, and any longer subarray starting at i will also have a zero product, so we can stop extending from this i. If the sign of the current subarray's product is positive, we update our maximum length.
class Solution { public int getMaxLen(int[] nums) { int n = nums.length; int maxLength = 0; for (int i = 0; i < n; i++) { int productSign = 1; for (int j = i; j < n; j++) { if (nums[j] == 0) { // Product becomes 0, which is not positive. // Any subarray extending further will also have a product of 0. break; } else if (nums[j] < 0) { productSign *= -1; } // If product is positive, update maxLength if (productSign > 0) { maxLength = Math.max(maxLength, j - i + 1); } } } return maxLength; }}Complexity
Time
O(n^2), where n is the number of elements in `nums`. The nested loops result in a quadratic number of operations as we check every possible subarray.
Space
O(1), as we only use a constant amount of extra space for variables like `maxLength` and `productSign`.
Trade-offs
Pros
Simple to understand and implement.
Correctly solves the problem for smaller inputs.
Cons
The O(n^2) time complexity is too slow for the given constraints (n <= 10^5) and will likely result in a 'Time Limit Exceeded' error on most platforms.
A highly efficient solution can be achieved using a single pass through the array, which is a form of dynamic programming with constant space. The key idea is to keep track of the length of the longest subarray ending at the current position that has a positive product, and similarly, the length of the one with a negative product. Based on the current number (positive, negative, or zero), we can determine the new lengths for these two categories.
Algorithm
- Initialize
maxLength = 0,positiveLen = 0(length of subarray ending here with positive product), andnegativeLen = 0(length of subarray ending here with negative product). - Iterate through each number
numin thenumsarray. - Case 1:
num == 0- A zero resets any running product. A subarray containing zero has a product of zero, which is not positive.
- Reset
positiveLen = 0andnegativeLen = 0.
- Case 2:
num > 0- Appending a positive number doesn't change the sign of the product.
- Increment
positiveLen. - If a negative product subarray existed (
negativeLen > 0), incrementnegativeLenas well.
- Case 3:
num < 0- Appending a negative number flips the sign of the product.
- The new positive-product subarray is formed by appending
numto the previous negative-product subarray. So,new_positiveLen = negativeLen + 1(ifnegativeLen > 0, else 0). - The new negative-product subarray is formed by appending
numto the previous positive-product subarray. So,new_negativeLen = positiveLen + 1. - We must use the values of
positiveLenandnegativeLenfrom before this step, so a temporary variable is needed to hold one of them during the swap.
- After each step, update the overall
maxLength = max(maxLength, positiveLen). - Return
maxLengthafter the loop.
Walkthrough
We iterate through the array, maintaining two counters: positiveLen and negativeLen. positiveLen stores the length of the longest subarray ending at the current index with a positive product, and negativeLen does the same for a negative product.
- If we see a
0, any subarray ending here will have a product of0. So, we must start fresh, resetting bothpositiveLenandnegativeLento0. - If we see a positive number, it extends both positive and negative product subarrays. We increment
positiveLen. We also incrementnegativeLenif it's already greater than zero. - If we see a negative number, it flips the signs. A new positive subarray is formed by adding the negative number to a previous negative subarray. A new negative subarray is formed by adding it to a previous positive subarray. We effectively swap the roles of
positiveLenandnegativeLenand increment them. For example, the newpositiveLenbecomes the oldnegativeLen + 1.
At each step of the iteration, the current positiveLen represents a valid subarray length, so we continuously update our global maxLength with it.
class Solution { public int getMaxLen(int[] nums) { int maxLength = 0; int positiveLen = 0; int negativeLen = 0; for (int num : nums) { if (num == 0) { positiveLen = 0; negativeLen = 0; } else if (num > 0) { positiveLen++; if (negativeLen > 0) { negativeLen++; } } else { // num < 0 int temp = positiveLen; // New positive length is from old negative length if (negativeLen > 0) { positiveLen = negativeLen + 1; } else { positiveLen = 0; } // New negative length is from old positive length negativeLen = temp + 1; } maxLength = Math.max(maxLength, positiveLen); } return maxLength; }}Complexity
Time
O(n), where n is the length of the array. We perform a single pass through the array.
Space
O(1). We only use a few variables (`maxLength`, `positiveLen`, `negativeLen`, `temp`) regardless of the input size.
Trade-offs
Pros
Optimal time complexity, solving the problem in a single pass.
Optimal space complexity, using only a constant number of variables.
Handles all edge cases like zeros and sequences of negative numbers gracefully.
Cons
The logic, especially the state transitions when a negative number is encountered, can be slightly non-intuitive at first glance.
Solutions
Solution
class Solution {public int getMaxLen(int[] nums) { int f1 = nums[0] > 0 ? 1 : 0; int f2 = nums[0] < 0 ? 1 : 0; int res = f1; for (int i = 1; i < nums.length; ++i) { if (nums[i] > 0) { ++f1; f2 = f2 > 0 ? f2 + 1 : 0; } else if (nums[i] < 0) { int pf1 = f1, pf2 = f2; f2 = pf1 + 1; f1 = pf2 > 0 ? pf2 + 1 : 0; } else { f1 = 0; f2 = 0; } res = Math.max(res, f1); } return res; }}class Solution {public: int getMaxLen(vector<int> &nums) { int f1 = nums[0] > 0 ? 1 : 0; int f2 = nums[0] < 0 ? 1 : 0; int res = f1; for (int i = 1; i < nums.size(); ++i) { if (nums[i] > 0) { ++f1; f2 = f2 > 0 ? f2 + 1 : 0; } else if (nums[i] < 0) { int pf1 = f1, pf2 = f2; f2 = pf1 + 1; f1 = pf2 > 0 ? pf2 + 1 : 0; } else { f1 = 0; f2 = 0; } res = max(res, f1); } return res; }};class Solution: def getMaxLen(self, nums: List[int]) -> int: f1 = 1 if nums[0] > 0 else 0 f2 = 1 if nums[0] < 0 else 0 res = f1 for num in nums[1:]: pf1, pf2 = f1, f2 if num > 0: f1 += 1 if f2 > 0: f2 += 1 else: f2 = 0 elif num < 0: pf1, pf2 = f1, f2 f2 = pf1 + 1 if pf2 > 0: f1 = pf2 + 1 else: f1 = 0 else: f1 = 0 f2 = 0 res = max(res, f1) return resVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.