Skip to content
advancedPhase 6 · Dynamic Programming

1D DP

Solve classic 1D DP problems like climbing stairs and house robber.

1h 15m
6 problems
Topic Progress0%

1D DP Patterns and Templates

1D DP problems use a single array where dp[i] represents the optimal solution for the subproblem ending at index i.

Pattern 1: Fibonacci-like

Problems where each state depends on the previous one or two states.

// Template: dp[i] = dp[i-1] + dp[i-2]
// Example: Climbing Stairs, Fibonacci
public int solveFibonacci(int n) {
    int[] dp = new int[n + 1];
    dp[0] = 0;
    dp[1] = 1;
    
    for (int i = 2; i <= n; i++) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    
    return dp[n];
}

Pattern 2: Maximum/Minimum Sum

Select elements to maximize/minimize sum with constraints.

// House Robber: Maximum sum of non-adjacent elements
// dp[i] = max value considering houses 0..i
public int rob(int[] nums) {
    int n = nums.length;
    if (n == 0) return 0;
    if (n == 1) return nums[0];
    
    int[] dp = new int[n];
    dp[0] = nums[0];
    dp[1] = Math.max(nums[0], nums[1]);
    
    for (int i = 2; i < n; i++) {
        dp[i] = Math.max(dp[i - 1],      // Skip current house
                         dp[i - 2] + nums[i]);  // Rob current house
    }
    
    return dp[n - 1];
}

// Space-optimized version
public int robOptimized(int[] nums) {
    int n = nums.length;
    if (n == 0) return 0;
    if (n == 1) return nums[0];
    
    int prev2 = nums[0];
    int prev1 = Math.max(nums[0], nums[1]);
    
    for (int i = 2; i < n; i++) {
        int curr = Math.max(prev1, prev2 + nums[i]);
        prev2 = prev1;
        prev1 = curr;
    }
    
    return prev1;
}

Pattern 3: Kadane's Algorithm (Maximum Subarray)

// Maximum contiguous subarray sum
// dp[i] = max subarray sum ending at index i
public int maxSubArray(int[] nums) {
    int maxSoFar = nums[0];
    int maxEndingHere = nums[0];
    
    for (int i = 1; i < nums.length; i++) {
        // Either extend previous subarray or start new one
        maxEndingHere = Math.max(nums[i], maxEndingHere + nums[i]);
        maxSoFar = Math.max(maxSoFar, maxEndingHere);
    }
    
    return maxSoFar;
}

Pattern 4: Decode Ways

// Count number of ways to decode a string of digits
// '1' -> 'A', '2' -> 'B', ..., '26' -> 'Z'
public int numDecodings(String s) {
    int n = s.length();
    int[] dp = new int[n + 1];
    dp[0] = 1;  // Empty string has 1 way
    dp[1] = s.charAt(0) != '0' ? 1 : 0;
    
    for (int i = 2; i <= n; i++) {
        // Single digit decode
        if (s.charAt(i - 1) != '0') {
            dp[i] += dp[i - 1];
        }
        
        // Two digit decode
        int twoDigit = Integer.parseInt(s.substring(i - 2, i));
        if (twoDigit >= 10 && twoDigit <= 26) {
            dp[i] += dp[i - 2];
        }
    }
    
    return dp[n];
}

When to Use 1D DP

  • Problem has sequential structure (array/string)
  • State depends on a constant number of previous states
  • You need to find optimal value (max/min/count)
  • Greedy doesn't work (counterexample exists)

Space Optimization Techniques

Why Optimize Space?

In many 1D DP problems, dp[i] only depends on a few previous values. We can reduce space from O(n) to O(1).

Observation

// If dp[i] = f(dp[i-1], dp[i-2], ..., dp[i-k])
// We only need to keep track of the last k values

Example: House Robber with O(1) Space

public int rob(int[] nums) {
    int n = nums.length;
    if (n == 0) return 0;
    if (n == 1) return nums[0];
    
    // Only need prev2 and prev1
    int prev2 = nums[0];
    int prev1 = Math.max(nums[0], nums[1]);
    
    for (int i = 2; i < n; i++) {
        int curr = Math.max(prev1, prev2 + nums[i]);
        prev2 = prev1;
        prev1 = curr;
    }
    
    return prev1;
}

Generic Pattern for Rolling Variables

// Instead of:
int[] dp = new int[n + 1];
for (int i = 0; i <= n; i++) {
    dp[i] = /* ... */;
}

// Use rolling variables:
int prev2 = baseValue1;
int prev1 = baseValue2;
for (int i = k; i <= n; i++) {
    int curr = /* depends on prev1, prev2, ... */;
    prev2 = prev1;
    prev1 = curr;
}
return prev1;  // or prev2 depending on the problem

Maximum Subarray with O(1) Space

public int maxSubArray(int[] nums) {
    int maxSoFar = nums[0];
    int maxEndingHere = nums[0];
    
    for (int i = 1; i < nums.length; i++) {
        maxEndingHere = Math.max(nums[i], maxEndingHere + nums[i]);
        maxSoFar = Math.max(maxSoFar, maxEndingHere);
    }
    
    return maxSoFar;
}

Common Mistakes in Space Optimization

  1. Forgetting base cases: Ensure initial values are correct
  2. Overwriting values: Update in correct order
  3. Wrong return value: Return the right variable (prev1 vs prev2)
  4. Not handling n < k: When n is smaller than the number of variables needed

Practice Problems

0/4solved
House Robber
Linear DP - Maximum Sum

You are a robber planning to rob houses along a street. Each house has a certain amount of money. The only constraint stopping you from robbing each of them is that adjacent houses have security systems connected. If two adjacent houses were broken into on the same night, the security system alerts the police. Given an integer array nums representing the amount of money of each house, return the maximum amount you can rob without alerting the police.

Example:

Input: nums = [1,2,3,1]

Output: 4

Rob house 1 (money = 1) and house 3 (money = 3). Total = 1 + 3 = 4.

Optimal Solution — O(n) time, O(1) space

1D DP with space optimization. dp[i] = max value considering houses 0..i. At each house, choose to skip or rob.

class Solution {
    public int rob(int[] nums) {
        int n = nums.length;
        if (n == 0) return 0;
        if (n == 1) return nums[0];
        
        int prev2 = nums[0];
        int prev1 = Math.max(nums[0], nums[1]);
        
        for (int i = 2; i < n; i++) {
            int curr = Math.max(prev1, prev2 + nums[i]);
            prev2 = prev1;
            prev1 = curr;
        }
        
        return prev1;
    }
}

Edge Cases:

  • Single house: return its value
  • Two houses: return the larger value
  • All zeros: return 0
  • Decreasing values: skip middle houses
Coin Change
1D DP - Knapsack Variant

You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money. Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example:

Input: coins = [1,5,10,25], amount = 30

Output: 2

25 + 5 = 30, so 2 coins.

Optimal Solution — O(amount × coins.length) time, O(amount) space

Bottom-up DP. dp[i] = minimum coins to make amount i.

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        dp[0] = 0;
        
        for (int i = 1; i <= amount; i++) {
            for (int coin : coins) {
                if (coin <= i) {
                    dp[i] = Math.min(dp[i], dp[i - coin] + 1);
                }
            }
        }
        
        return dp[amount] > amount ? -1 : dp[amount];
    }
}

Edge Cases:

  • amount = 0: return 0
  • No coins: return -1
  • Impossible to make amount: return -1
Decode Ways
1D DP - Counting

A message consisting of letters is encoded as: 'A' -> 1, 'B' -> 2, ..., 'Z' -> 26. Given a string s of digits, return the number of ways to decode it.

Example:

Input: s = "226"

Output: 3

"226" can be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).

Optimal Solution — O(n) time, O(1) space

DP similar to climbing stairs. dp[i] = number of ways to decode s[0..i-1].

class Solution {
    public int numDecodings(String s) {
        if (s.charAt(0) == '0') return 0;
        int n = s.length();
        int prev2 = 1;  // dp[0]
        int prev1 = 1;  // dp[1]
        
        for (int i = 2; i <= n; i++) {
            int curr = 0;
            int oneDigit = s.charAt(i - 1) - '0';
            int twoDigit = Integer.parseInt(s.substring(i - 2, i));
            
            if (oneDigit >= 1) curr += prev1;
            if (twoDigit >= 10 && twoDigit <= 26) curr += prev2;
            
            prev2 = prev1;
            prev1 = curr;
        }
        
        return prev1;
    }
}

Edge Cases:

  • Starts with '0': return 0
  • Single digit: 1 way if not '0'
  • '10': 1 way
  • '27': 1 way (only '2','7')
House Robber II
1D DP - Circular

All houses are arranged in a circle. If you rob the first house, you cannot rob the last. Return the maximum amount you can rob.

Example:

Input: nums = [2,3,2]

Output: 3

Rob house 2 (money = 3). Cannot rob house 1 and 3 together because they are adjacent in circle.

Optimal Solution — O(n) time, O(1) space

Two cases: rob houses [0..n-2] or [1..n-1]. Take max.

class Solution {
    public int rob(int[] nums) {
        int n = nums.length;
        if (n == 1) return nums[0];
        return Math.max(robRange(nums, 0, n - 2), robRange(nums, 1, n - 1));
    }
    
    private int robRange(int[] nums, int start, int end) {
        int prev2 = 0, prev1 = 0;
        for (int i = start; i <= end; i++) {
            int curr = Math.max(prev1, prev2 + nums[i]);
            prev2 = prev1;
            prev1 = curr;
        }
        return prev1;
    }
}

Edge Cases:

  • Single house: return its value
  • Two houses: return max of both
  • All zeros: return 0

Quiz

1. In House Robber, what does dp[i] represent?

Question 1 options

2. What is the state transition for House Robber?

Question 2 options

3. What is the primary purpose of 1D Dynamic Programming?

Question 3 options

4. What is a common mistake when implementing 1D Dynamic Programming?

Question 4 options

Flashcards

Question

What is Kadane's Algorithm used for?

Answer

Finding the maximum sum contiguous subarray in O(n) time. dp[i] = max(nums[i], dp[i-1] + nums[i]).

Question

When can you optimize 1D DP space from O(n) to O(1)?

Answer

When dp[i] depends only on a constant number of previous states (e.g., dp[i-1] and dp[i-2]). Use rolling variables instead of array.

Question

What is 1D Dynamic Programming?

Answer

1D Dynamic Programming is a key concept in software engineering.

Question

When to use 1D Dynamic Programming?

Answer

Use 1D Dynamic Programming when building production systems that require reliability, scalability, and maintainability.

Question

1D Dynamic Programming best practices

Answer

Follow SOLID principles, write clean code, test thoroughly, document decisions, and monitor in production.

Revision Notes

Key Takeaways

  • 1.1D DP is for sequential problems with constant-width dependency
  • 2.Always define dp[i] clearly before coding
  • 3.Space optimization is possible when recurrence has limited dependency
  • 4.Kadane's algorithm is the classic O(n) maximum subarray solution

Interview Tips

  • Start with O(n) space solution, then optimize
  • Draw out dp table for small inputs to verify
  • Explain the state transition clearly
  • Handle edge cases: empty array, single element

Cheat Sheet

1D DP Cheat Sheet

Common Patterns:

  • Fibonacci-like: dp[i] = dp[i-1] + dp[i-2]
  • Max sum non-adjacent: dp[i] = max(dp[i-1], dp[i-2] + nums[i])
  • Kadane's: maxEndingHere = max(nums[i], maxEndingHere + nums[i])

Space Optimization:

  • If dp[i] depends on dp[i-1] and dp[i-2], use two variables
  • If dp[i] depends on dp[i-1..i-k], use k variables

Template:

int prev2 = base1;
int prev1 = base2;
for (int i = k; i < n; i++) {
    int curr = f(prev1, prev2, ...);
    prev2 = prev1;
    prev1 = curr;
}
return prev1;

Key Insight:

  • Always ask: "What decision do I make at each step?"