package com.freetymekiyan.algorithms.level.medium;
/**
* Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
* <p>
* For example, given the array [-2,1,-3,4,-1,2,1,-5,4],
* the contiguous subarray [4,-1,2,1] has the largest sum = 6.
* <p>
* click to show more practice.
* <p>
* More practice:
* If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which
* is more subtle.
* <p>
* Company Tags: LinkedIn, Bloomberg, Microsoft
* Tags: Array, Dynamic Programming, Divide and Conquer
* Similar Problems: (E) Best Time to Buy and Sell Stock, (M) Maximum Product Subarray
*/
public class MaximumSubarray {
/**
* DP.
* Recurrence Relation:
* So if we know the max sum ending at i-1. How can we get the max sum ending at i?
* Since the sub array is contiguous. It must include nums[i].
* So max sum ending at i is either nums[i] or nums[i] + max sum ending at i-1, whichever is larger.
* Then we update the overall result with the new max.
*/
public int maxSubArray(int[] nums) {
if (nums == null || nums.length == 0) return 0;
int max = nums[0];
int maxEndingHere = nums[0];
for (int i = 1; i < nums.length; ++i) {
maxEndingHere = Math.max(maxEndingHere + nums[i], nums[i]);
max = Math.max(max, maxEndingHere);
}
return max;
}
/**
* DP, O(n) Time, O(1) Space
* If A[i] < 0 && currentMax + A[i] < 0, should recalculate max
* If A[i] < 0 && currentMax + A[i] >= 0, continue
* currentMax = max(currentMax + A[i], A[i])
* maxSubArr = max(currentMax, maxSubArr)
*/
public int maxSubArraySum(int[] A) {
if (A == null || A.length == 0) {
return 0;
}
int curMax = A[0];
int max = A[0];
for (int i = 1; i < A.length; i++) { // note that i starts from 1
curMax = Math.max(curMax + A[i], A[i]);
max = Math.max(curMax, max);
}
return max;
}
/**
* DP, O(n) Time, O(n) Space
*/
public int maxSubArraySumB(int[] A) {
if (A == null || A.length == 0) {
return 0;
}
int[] s = new int[A.length]; // save max sum so far in an array
s[0] = A[0];
int max = A[0];
for (int i = 1; i < A.length; i++) {
s[i] = s[i - 1] > 0 ? (A[i] + s[i - 1]) : A[i];
max = Math.max(max, s[i]);
}
return max;
}
/**
* Not asking sum, but the range
* If A[i] < 0, current sum + A[i] >= 0, we can continue addition because
* the positive sum would still contribute to positiveness of the subarray.
* If A[i] < 0, current sum + A[i] < 0, the current subarray has to end.
*/
public int[] maxSubArrayB(int[] A) {
int beginTemp = 0; // save the temporary begining index
int begin = 0; // begining index
int end = 0; // ending index
int maxSoFar = A[0]; // max sum of previous sequence
int maxEndingHere = A[0]; // max sum of this group
for (int i = 1; i < A.length; i++) {
if (maxEndingHere < 0) { // last A[i] is too small
maxEndingHere = A[i];
beginTemp = i; // update begin temp
} else {
maxEndingHere += A[i];
}
if (maxEndingHere >= maxSoFar) { // update max so far
maxSoFar = maxEndingHere;
begin = beginTemp; // save index range
end = i;
}
}
return new int[]{begin, end, maxSoFar};
}
}