/**
 * Find the contiguous subarray within an array (containing at least one
 * number) which has the largest product.
 * 
 * For example, given the array [2,3,-2,4],
 * the contiguous subarray [2,3] has the largest product = 6.
 * 
 * Tags: Array, DP
 */
class MaxProductSubArr {
    
    /**
     * DP, update according to A[i]
     * f(k) = Largest product subarray, from index 0 up to k.
     * g(k) = Smallest product subarray, from index 0 up to k.
     * 
     * f(k) = max( f(k-1) * A[k], A[k], g(k-1) * A[k] )
     * g(k) = min( g(k-1) * A[k], A[k], f(k-1) * A[k] )
     */
    public int maxProduct(int[] A) {
        if (A == null || A.length == 0) return 0;

        int max = A[0], min = A[0], res = A[0];
        for (int i = 1; i < A.length; i++) {
            int preMax = max, preMin = min; // results of last loop
            max = Math.max(Math.max(A[i], preMax * A[i]), preMin * A[i]);
            min = Math.min(Math.min(A[i], preMax * A[i]), preMin * A[i]);
            res = Math.max(max, res);
        }
        return res;
    }
}

package com.freetymekiyan.algorithms.level.medium;

/**
 * Find the contiguous subarray within an array (containing at least one number) which has the largest product.
 * <p>
 * For example, given the array [2,3,-2,4],
 * the contiguous subarray [2,3] has the largest product = 6.
 * <p>
 * Company Tags: LinkedIn
 * Tags: Array, Dynamic Programming
 * Similar Problems: (M) Maximum Subarray, (E) House Robber, (M) Product of Array Except Self
 */
public class MaxProductSubArray {

    /**
     * DP. Bottom-up.
     * We need to track both maximum product and minimum product.
     * In case a minimum product multiply by a negative number and become maximum.
     * f(k): Largest product subarray, from index 0 up to k.
     * g(k): Smallest product subarray, from index 0 up to k.
     * Similar to max sum subarray, the max product candidates are:
     * 1. Build on previous subarray max product: f(k-1) * A[k]
     * 2. Build on previous subarray min product: g(k-1) * A[k]
     * 3. Start from right here: A[k]
     * Recurrence Relation:
     * f(k) = max( f(k-1) * A[k], A[k], g(k-1) * A[k] )
     * g(k) = min( g(k-1) * A[k], A[k], f(k-1) * A[k] )
     * Base:
     * Initialize max, min and final result as the first number.
     */
    public int maxProduct(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }

        int max = nums[0], min = nums[0], res = nums[0];
        for (int i = 1; i < nums.length; i++) {
            int preMax = max, preMin = min; // IMPORTANT: max and min need tp be stored.
            max = Math.max(Math.max(nums[i], preMax * nums[i]), preMin * nums[i]);
            min = Math.min(Math.min(nums[i], preMax * nums[i]), preMin * nums[i]);
            res = Math.max(max, res);
        }
        return res;
    }
}

update max product subarray