Pattern 1c: Subarray Optimization
Core Concept: Find optimal contiguous subarray ending at each position. Key distinction:
dp[i]must include element at indexi.
The Pattern
Imagine scanning an array left-to-right:
- At each position
i, you must includenums[i] - Decision: Extend previous subarray OR start fresh from
i - Keep track of global maximum separately
- Formula:
dp[i] = max(dp[i-1] + nums[i], nums[i])
This is Kadane's Algorithm - the foundation of subarray optimization.
Pattern Recognition
Keywords: "contiguous subarray", "maximum sum", "must include", "ending at position i"
Structure:
- Must find optimal contiguous subarray
- State definition: "best answer ending at position i"
- Decision: extend vs. start fresh
- Track global optimum separately from dp[i]
State: dp[i] = best answer for subarray ending at position i
Key Transition:
dp[i] = Math.max(
dp[i-1] + nums[i], // Extend previous subarray
nums[i] // Start fresh from current element
)
maxSum = Math.max(maxSum, dp[i]); // Track global best
The Critical Distinction
This is Pattern 1c's defining characteristic:
Pattern 1a & 1b: dp[i] = best answer up to position i
- May or may not include element at index i
- Cumulative best so far
Pattern 1c: dp[i] = best answer ending at position i
- MUST include element at index i
- Local best at this position
Example (Maximum Subarray):
nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
dp[4] (Pattern 1c) = best sum ENDING at index 4
= [4, -1] = 3 (includes nums[4])
maxSum (global) = best sum UP TO index 4
= [4] = 4 (may not include nums[4])
Problems in This Category
Core Problems
- Maximum Subarray (LC 53) - The canonical Kadane's Algorithm
- Maximum Product Subarray (LC 152) - Track both max and min
- Maximum Sum Circular Subarray (LC 918) - Circular array variant
Why Subarrays Need This Approach
Why not just use dp[i] = max(dp[i-1], nums[i])?
Because we need to know if the previous subarray is still worth extending.
If dp[i-1] is negative and nums[i] is positive, starting fresh is better!
Example:
nums = [-5, 3]
dp[0] = -5
dp[1] = ?
Option 1: dp[1] = dp[0] + nums[1] = -5 + 3 = -2
Option 2: dp[1] = nums[1] = 3 ✓ (start fresh)
We choose 3 because extending the negative sum makes things worse!
Template Code
Standard Kadane's Algorithm
public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
int maxSum = dp[0];
for (int i = 1; i < nums.length; i++) {
// Either extend previous subarray or start fresh
dp[i] = Math.max(dp[i-1] + nums[i], nums[i]);
// Track global maximum
maxSum = Math.max(maxSum, dp[i]);
}
return maxSum;
}
Space-Optimized Kadane's
public int maxSubArrayOptimized(int[] nums) {
int currentSum = nums[0]; // dp[i]
int maxSum = nums[0]; // global max
for (int i = 1; i < nums.length; i++) {
// Either extend or start fresh
currentSum = Math.max(currentSum + nums[i], nums[i]);
// Update global max
maxSum = Math.max(maxSum, currentSum);
}
return maxSum;
}
The Decision Logic
// At each position i:
if (dp[i-1] + nums[i] > nums[i]) {
// Previous subarray is worth extending
dp[i] = dp[i-1] + nums[i];
} else {
// Better to start fresh from current element
dp[i] = nums[i];
}
// Simplified:
dp[i] = Math.max(dp[i-1] + nums[i], nums[i]);
Key Insights
Why this works:
- Local Decision: At each position, make locally optimal choice
- Extend or Reset: If previous sum helps, extend; otherwise reset
- Global Tracking: Keep separate variable for best answer seen so far
The Power:
- ✅ O(n) time - single pass through array
- ✅ O(1) space - only need two variables
- ✅ Handles all negative arrays correctly
- ✅ Works for all integer values
Edge Cases:
nums = [-2, -1] → maxSum = -1 (best single element)
nums = [1] → maxSum = 1
nums = [5, -3, 5] → maxSum = 7 (entire array)
Variations
Maximum Product Subarray
Twist: Track BOTH maximum and minimum (negative × negative = positive!)
public int maxProduct(int[] nums) {
int maxDP = nums[0];
int minDP = nums[0];
int result = nums[0];
for (int i = 1; i < nums.length; i++) {
// Negative number can flip min to max
int tempMax = maxDP;
maxDP = Math.max(nums[i], Math.max(maxDP * nums[i], minDP * nums[i]));
minDP = Math.min(nums[i], Math.min(tempMax * nums[i], minDP * nums[i]));
result = Math.max(result, maxDP);
}
return result;
}
Common Mistakes
Mistake 1: Forgetting to track global max
// WRONG
dp[i] = Math.max(dp[i-1] + nums[i], nums[i]);
return dp[n-1]; // ❌ This is only the best ENDING at last position!
// CORRECT
maxSum = Math.max(maxSum, dp[i]); // Track global max
return maxSum; // ✓ Return overall best
Mistake 2: Not starting fresh when needed
// WRONG
dp[i] = dp[i-1] + nums[i]; // ❌ Always extending, even when harmful
// CORRECT
dp[i] = Math.max(dp[i-1] + nums[i], nums[i]); // ✓ Start fresh if better
Mistake 3: Confusing with Pattern 1b
// Pattern 1b (House Robber):
dp[i] = max(dp[i-1], dp[i-2] + nums[i]) // Choose between i-1 and i-2
// Pattern 1c (Kadane):
dp[i] = max(dp[i-1] + nums[i], nums[i]) // Extend i-1 or start fresh
Key Differences from Other Patterns
vs. Pattern 1a (Simple Accumulation):
- 1a:
dp[i]= count/ways up to i - 1c:
dp[i]= best sum ending at i
vs. Pattern 1b (Selection with Skip):
- 1b:
dp[i]= best with skip constraint up to i - 1c:
dp[i]= best contiguous subarray ending at i
Key: Pattern 1c requires contiguity (no gaps allowed in subarray)
Learning Path
Phase 1: Foundation
- Maximum Subarray (LC 53) - Master Kadane's Algorithm
- Best Time to Buy and Sell Stock (LC 121) - Kadane's variation
Phase 2: Variations 3. Maximum Product Subarray (LC 152) - Track both max and min 4. Maximum Sum Circular Subarray (LC 918) - Handle circular arrays
Key Insight: Once you understand "ending at i" vs "up to i", you'll know when to use Kadane's!
Final Thoughts
Kadane's Algorithm is a masterpiece:
- Solves a complex problem in O(n) time, O(1) space
- Clean, elegant logic
- Handles all edge cases naturally
- Foundation for many subarray optimization problems
Remember the core idea:
At each position, decide: Extend or start fresh?
Next Steps
After mastering Pattern 1c:
- ✅ You'll recognize "contiguous subarray" → Kadane's pattern
- ✅ You'll understand "ending at i" state definition
- ✅ You'll see Kadane's variations in many problems
Master these 3 subpatterns (1a, 1b, 1c) and you've mastered Pattern 1!