Maximum Product Subarray

Question

Find the contiguous subarray within an array (containing at least one number) which has the largest product.
Example For example, given the array [2,3,-2,4], the contiguous subarray [2,3] has the largest product = 6.
Tags
LinkedIn Dynamic Programming Subarray
Related Problems
Medium Best Time to Buy and Sell Stock 41 % Medium Maximum Subarray Difference 23 % Easy Minimum Subarray 38 % Medium Maximum Subarray II

Analysis

本题其实是Maximum Subarray Sum问题的延伸,不同的是这里需要考虑元素的符号。
DP的四要素
  • 状态:
    • max_product[i]: 以nums[i]结尾的max subarray product
    • min_product[i]: 以nums[i]结尾的min subarray product
  • 方程:
    • max_product[i] = getMax(max_product[i-1] * nums[i], min_product[i-1] * nums[i], nums[i])
    • min_product[i] = getMin(max_product[i-1] * nums[i], min_product[i-1] * nums[i], nums[i])
  • 初始化:
    • max_product[0] = min_product[0] = nums[0]
  • 结果:
    • 每次循环中 max_product[i] 的最大值

Solution

DP, with O(n) space, O(n) time
public class Solution {
/**
* @param nums: an array of integers
* @return: an integer
*/
public int maxProduct(int[] nums) {
int[] max = new int[nums.length]; // maximum product ending with nums[i]
int[] min = new int[nums.length]; // minimum product ending with nums[i]
max[0] = min[0] = nums[0];
int result = max[0];
for (int i = 1; i < nums.length; i++) {
max[i] = min[i] = nums[i];
if (nums[i] > 0) { // 区分nums[i]的符号
max[i] = Math.max(max[i], nums[i] * max[i - 1]);
min[i] = Math.min(min[i], nums[i] * min[i - 1]);
} else {
max[i] = Math.max(max[i], nums[i] * min[i - 1]);
min[i] = Math.min(min[i], nums[i] * max[i - 1]);
}
result = Math.max(max[i], result);
}
return result;
}
}
DP - Simplified - O(n) space, O(1) time
class Solution {
public int maxProduct(int[] nums) {
if (nums == null || nums.length == 0) return 0;
int n = nums.length;
int[] maxProduct = new int[n]; // max subarray product ending in nums[i]
int[] minProduct = new int[n]; // min subarray product ending in nums[i]
maxProduct[0] = nums[0];
minProduct[0] = nums[0];
int max = maxProduct[0];
for (int i = 1; i < n; i++) {
maxProduct[i] = Math.max(nums[i], Math.max(maxProduct[i - 1] * nums[i], minProduct[i - 1] * nums[i]));
minProduct[i] = Math.min(nums[i], Math.min(maxProduct[i - 1] * nums[i], minProduct[i - 1] * nums[i]));
max = Math.max(maxProduct[i], max);
}
return max;
}
}
DP, improved, with O(1) space, O(n) time
public class Solution {
/**
* @param nums: an array of integers
* @return: an integer
*/
public int maxProduct(int[] nums) {
int result, currentMax, currentMin;
result = currentMax = currentMin = nums[0];
for (int i = 1; i < nums.length; i++) {
int temp = currentMax;
currentMax = Math.max(nums[i], Math.max(currentMax * nums[i], currentMin * nums[i]));
currentMin = Math.min(nums[i], Math.min(temp * nums[i], currentMin * nums[i]));
result = Math.max(currentMax, result);
}
return result;
}
}
O(1) space, O(n) time
class Solution {
public int maxProduct(int[] nums) {
if (nums == null || nums.length == 0){
return 0;
}
int maxPre = nums[0], minPre = nums[0], max = nums[0], maxHere, minHere;
for (int i = 1; i < nums.length; i++){
maxHere = Math.max(nums[i], Math.max(nums[i] * maxPre, nums[i] * minPre));
minHere = Math.min(nums[i], Math.min(nums[i] * maxPre, nums[i] * minPre));
max = Math.max(max, maxHere);
maxPre = maxHere;
minPre = minHere;
}
return max;
}
}
Same idea but a more concise implementation
int maxProduct(int A[], int n) {
// store the result that is the max we have found so far
int r = A[0];
// imax/imin stores the max/min product of
// subarray that ends with the current number A[i]
for (int i = 1, imax = r, imin = r; i < n; i++) {
// multiplied by a negative makes big number smaller, small number bigger
// so we redefine the extremums by swapping them
if (A[i] < 0)
swap(imax, imin);
// max/min product for the current number is either the current number itself
// or the max/min by the previous number times the current one
imax = max(A[i], imax * A[i]);
imin = min(A[i], imin * A[i]);
// the newly computed max value is a candidate for our global result
r = max(r, imax);
}
return r;
}

Reference