1. Introduction to Dynamic Programming
Dynamic Programming (DP) is a core topic in technical coding interviews at Google, Amazon, Microsoft, and Meta. Mastering DP boils down to recognizing recurring patterns: 1D DP, 2D Grid DP, Knapsack variants, and String Alignment.
Below are top Dynamic Programming questions with complete, production-grade solutions in C++, Java, Python, and JavaScript.
2. Core DP Questions
Q1. Climbing Stairs
Question: You are climbing a staircase with n steps. Each time you can climb 1 or 2 steps. In how many distinct ways can you climb to the top?
Intuition: dp[i] = dp[i-1] + dp[i-2]. To reach step i, you must come from i-1 or i-2.
cppint climbStairs(int n) { if (n <= 2) return n; int a = 1, b = 2; for (int i = 3; i <= n; i++) { int temp = a + b; a = b; b = temp; } return b; }
javapublic int climbStairs(int n) { if (n <= 2) return n; int a = 1, b = 2; for (int i = 3; i <= n; i++) { int temp = a + b; a = b; b = temp; } return b; }
pythondef climbStairs(n: int) -> int: if n <= 2: return n a, b = 1, 2 for _ in range(3, n + 1): a, b = b, a + b return b
javascriptfunction climbStairs(n) { if (n <= 2) return n; let a = 1, b = 2; for (let i = 3; i <= n; i++) { let temp = a + b; a = b; b = temp; } return b; }
Time Complexity: O(n) | Space Complexity: O(1)
Q2. Coin Change (Minimum Coins)
Question: Return the fewest number of coins that you need to make up amount amount. If impossible, return -1.
cpp#include <vector> #include <algorithm> using namespace std; int coinChange(vector<int>& coins, int amount) { vector<int> dp(amount + 1, amount + 1); dp[0] = 0; for (int i = 1; i <= amount; i++) { for (int c : coins) { if (i >= c) dp[i] = min(dp[i], 1 + dp[i - c]); } } return dp[amount] > amount ? -1 : dp[amount]; }
javaimport java.util.Arrays; public int coinChange(int[] coins, int amount) { int max = amount + 1; int[] dp = new int[amount + 1]; Arrays.fill(dp, max); dp[0] = 0; for (int i = 1; i <= amount; i++) { for (int c : coins) { if (i >= c) dp[i] = Math.min(dp[i], 1 + dp[i - c]); } } return dp[amount] > amount ? -1 : dp[amount]; }
pythondef coinChange(coins: list, amount: int) -> int: dp = [float('inf')] * (amount + 1) dp[0] = 0 for i in range(1, amount + 1): for c in coins: if i >= c: dp[i] = min(dp[i], 1 + dp[i - c]) return dp[amount] if dp[amount] != float('inf') else -1
javascriptfunction coinChange(coins, amount) { let dp = new Array(amount + 1).fill(Infinity); dp[0] = 0; for (let i = 1; i <= amount; i++) { for (let c of coins) { if (i >= c) dp[i] = Math.min(dp[i], 1 + dp[i - c]); } } return dp[amount] !== Infinity ? dp[amount] : -1; }
Time Complexity: O(amount x len(coins)) | Space Complexity: O(amount)
Q3. Longest Increasing Subsequence (LIS)
Question: Find the length of the longest strictly increasing subsequence in an integer array nums.
cppint lengthOfLIS(vector<int>& nums) { if (nums.empty()) return 0; int n = nums.size(); vector<int> dp(n, 1); int maxLen = 1; for (int i = 1; i < n; i++) { for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) { dp[i] = max(dp[i], 1 + dp[j]); } } maxLen = max(maxLen, dp[i]); } return maxLen; }
javapublic int lengthOfLIS(int[] nums) { if (nums.length == 0) return 0; int n = nums.length; int[] dp = new int[n]; Arrays.fill(dp, 1); int maxLen = 1; for (int i = 1; i < n; i++) { for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) { dp[i] = Math.max(dp[i], 1 + dp[j]); } } maxLen = Math.max(maxLen, dp[i]); } return maxLen; }
pythondef lengthOfLIS(nums: list) -> int: if not nums: return 0 n = len(nums) dp = [1] * n for i in range(1, n): for j in range(i): if nums[i] > nums[j]: dp[i] = max(dp[i], 1 + dp[j]) return max(dp)
javascriptfunction lengthOfLIS(nums) { if (!nums || nums.length === 0) return 0; let n = nums.length; let dp = new Array(n).fill(1); let maxLen = 1; for (let i = 1; i < n; i++) { for (let j = 0; j < i; j++) { if (nums[i] > nums[j]) { dp[i] = Math.max(dp[i], 1 + dp[j]); } } maxLen = Math.max(maxLen, dp[i]); } return maxLen; }
Time Complexity: O(n^2) | Space Complexity: O(n)
Q4. 0/1 Knapsack Problem
Question: Given weights and values of N items, put these items in a knapsack of capacity W to get the maximum total value in the knapsack.
cppint knapsack(int W, vector<int>& wt, vector<int>& val, int n) { vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0)); for (int i = 1; i <= n; i++) { for (int w = 1; w <= W; w++) { if (wt[i - 1] <= w) { dp[i][w] = max(val[i - 1] + dp[i - 1][w - wt[i - 1]], dp[i - 1][w]); } else { dp[i][w] = dp[i - 1][w]; } } } return dp[n][W]; }
javapublic int knapsack(int W, int[] wt, int[] val, int n) { int[][] dp = new int[n + 1][W + 1]; for (int i = 1; i <= n; i++) { for (int w = 1; w <= W; w++) { if (wt[i - 1] <= w) { dp[i][w] = Math.max(val[i - 1] + dp[i - 1][w - wt[i - 1]], dp[i - 1][w]); } else { dp[i][w] = dp[i - 1][w]; } } } return dp[n][W]; }
pythondef knapsack(W, wt, val, n): dp = [[0] * (W + 1) for _ in range(n + 1)] for i in range(1, n + 1): for w in range(1, W + 1): if wt[i - 1] <= w: dp[i][w] = max(val[i - 1] + dp[i - 1][w - wt[i - 1]], dp[i - 1][w]) else: dp[i][w] = dp[i - 1][w] return dp[n][W]
javascriptfunction knapsack(W, wt, val, n) { let dp = Array.from({ length: n + 1 }, () => new Array(W + 1).fill(0)); for (let i = 1; i <= n; i++) { for (let w = 1; w <= W; w++) { if (wt[i - 1] <= w) { dp[i][w] = Math.max(val[i - 1] + dp[i - 1][w - wt[i - 1]], dp[i - 1][w]); } else { dp[i][w] = dp[i - 1][w]; } } } return dp[n][W]; }
Time Complexity: O(n x W) | Space Complexity: O(n x W)
Q5. House Robber
Question: You are a robber planning to rob houses along a street. Adjacent houses have security systems connected, so you cannot rob two adjacent houses. Return the maximum money you can rob.
Intuition: At house i, you either rob house i (and take nums[i] + dp[i-2]) or skip it (and take dp[i-1]). State relation: dp[i] = max(dp[i-1], nums[i] + dp[i-2]).
cppint rob(vector<int>& nums) { int prev1 = 0, prev2 = 0; for (int num : nums) { int temp = max(prev1, prev2 + num); prev2 = prev1; prev1 = temp; } return prev1; }
javapublic int rob(int[] nums) { int prev1 = 0, prev2 = 0; for (int num : nums) { int temp = Math.max(prev1, prev2 + num); prev2 = prev1; prev1 = temp; } return prev1; }
pythondef rob(nums: list) -> int: prev1 = prev2 = 0 for num in nums: temp = max(prev1, prev2 + num) prev2 = prev1 prev1 = temp return prev1
javascriptfunction rob(nums) { let prev1 = 0, prev2 = 0; for (let num of nums) { let temp = Math.max(prev1, prev2 + num); prev2 = prev1; prev1 = temp; } return prev1; }
Time Complexity: O(n) | Space Complexity: O(1)
Q6. Longest Common Subsequence (LCS)
Question: Given two strings text1 and text2, return the length of their longest common subsequence.
cppint longestCommonSubsequence(string text1, string text2) { int m = text1.size(), n = text2.size(); vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0)); for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (text1[i - 1] == text2[j - 1]) dp[i][j] = 1 + dp[i - 1][j - 1]; else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); } } return dp[m][n]; }
javapublic int longestCommonSubsequence(String text1, String text2) { int m = text1.length(), n = text2.length(); int[][] dp = new int[m + 1][n + 1]; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (text1.charAt(i - 1) == text2.charAt(j - 1)) dp[i][j] = 1 + dp[i - 1][j - 1]; else dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } return dp[m][n]; }
pythondef longestCommonSubsequence(text1: str, text2: str) -> int: m, n = len(text1), len(text2) dp = [[0] * (n + 1) for _ in range(m + 1)] for i in range(1, m + 1): for j in range(1, n + 1): if text1[i - 1] == text2[j - 1]: dp[i][j] = 1 + dp[i - 1][j - 1] else: dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) return dp[m][n]
javascriptfunction longestCommonSubsequence(text1, text2) { let m = text1.length, n = text2.length; let dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0)); for (let i = 1; i <= m; i++) { for (let j = 1; j <= n; j++) { if (text1[i - 1] === text2[j - 1]) dp[i][j] = 1 + dp[i - 1][j - 1]; else dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } return dp[m][n]; }
Time Complexity: O(m x n) | Space Complexity: O(m x n)
Q7. Edit Distance (Levenshtein Distance)
Question: Given two strings word1 and word2, return the minimum number of operations (insert, delete, replace) required to convert word1 into word2.
cppint minDistance(string word1, string word2) { int m = word1.size(), n = word2.size(); vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0)); for (int i = 0; i <= m; i++) dp[i][0] = i; for (int j = 0; j <= n; j++) dp[0][j] = j; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (word1[i - 1] == word2[j - 1]) dp[i][j] = dp[i - 1][j - 1]; else dp[i][j] = 1 + min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]}); } } return dp[m][n]; }
javapublic int minDistance(String word1, String word2) { int m = word1.length(), n = word2.length(); int[][] dp = new int[m + 1][n + 1]; for (int i = 0; i <= m; i++) dp[i][0] = i; for (int j = 0; j <= n; j++) dp[0][j] = j; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (word1.charAt(i - 1) == word2.charAt(j - 1)) dp[i][j] = dp[i - 1][j - 1]; else dp[i][j] = 1 + Math.min(dp[i - 1][j], Math.min(dp[i][j - 1], dp[i - 1][j - 1])); } } return dp[m][n]; }
pythondef minDistance(word1: str, word2: str) -> int: m, n = len(word1), len(word2) dp = [[0] * (n + 1) for _ in range(m + 1)] for i in range(m + 1): dp[i][0] = i for j in range(n + 1): dp[0][j] = j for i in range(1, m + 1): for j in range(1, n + 1): if word1[i - 1] == word2[j - 1]: dp[i][j] = dp[i - 1][j - 1] else: dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) return dp[m][n]
javascriptfunction minDistance(word1, word2) { let m = word1.length, n = word2.length; let dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0)); for (let i = 0; i <= m; i++) dp[i][0] = i; for (let j = 0; j <= n; j++) dp[0][j] = j; for (let i = 1; i <= m; i++) { for (let j = 1; j <= n; j++) { if (word1[i - 1] === word2[j - 1]) dp[i][j] = dp[i - 1][j - 1]; else dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]); } } return dp[m][n]; }
Time Complexity: O(m x n) | Space Complexity: O(m x n)
3. Summary Table
| Problem | Pattern | Time | Space |
|---|---|---|---|
| Climbing Stairs | 1D DP (Fibonacci) | O(n) | O(1) |
| Coin Change | Unbounded Knapsack | O(amount x n) | O(amount) |
| LIS | Subsequence DP | O(n^2) | O(n) |
| 0/1 Knapsack | 2D Subset DP | O(n x W) | O(n x W) |
| House Robber | 1D Non-adjacent Choice | O(n) | O(1) |
| LCS | 2D String Alignment | O(m x n) | O(m x n) |
| Edit Distance | 2D String Transformation | O(m x n) | O(m x n) |
Practice all DP problems on DSAMaster's practice platform.
