Code Templates
Master dynamic programming with these optimized code templates. Use them as starting points for solving DP problems across different programming languages and patterns.
1D Array DP Templates
The Main Template: Fibonacci Numbers
Description: This template demonstrates the core DP pattern for linear sequences. It’s the foundation for problems like Climbing Stairs, House Robber, and Jump Game.
/**
* Find number of ways to climb n stairs (1 or 2 steps)
* @param {number} n - Number of stairs
* @returns {number} Number of ways
*/
function climbStairs(n) {
if (n <= 1) return 1;
// Space optimized Fibonacci
let prev2 = 1, prev1 = 1;
for (let i = 2; i <= n; i++) {
const current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
}
return prev1;
}def climb_stairs(n):
"""
Find number of ways to climb n stairs (1 or 2 steps)
"""
if n <= 1:
return 1
# Space optimized Fibonacci
prev2, prev1 = 1, 1
for i in range(2, n + 1):
current = prev1 + prev2
prev2, prev1 = prev1, current
return prev1public class Solution {
/**
* Find number of ways to climb n stairs (1 or 2 steps)
*/
public int climbStairs(int n) {
if (n <= 1) return 1;
// Space optimized Fibonacci
int prev2 = 1, prev1 = 1;
for (int i = 2; i <= n; i++) {
int current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
}
return prev1;
}
}class Solution {
public:
/**
* Find number of ways to climb n stairs (1 or 2 steps)
*/
int climbStairs(int n) {
if (n <= 1) return 1;
// Space optimized Fibonacci
int prev2 = 1, prev1 = 1;
for (int i = 2; i <= n; i++) {
int current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
}
return prev1;
}
};func climbStairs(n int) int {
if n <= 1 {
return 1
}
// Space optimized Fibonacci
prev2, prev1 := 1, 1
for i := 2; i <= n; i++ {
current := prev1 + prev2
prev2, prev1 = prev1, current
}
return prev1
}def climb_stairs(n)
return 1 if n <= 1
# Space optimized Fibonacci
prev2, prev1 = 1, 1
(2..n).each do |i|
current = prev1 + prev2
prev2, prev1 = prev1, current
end
prev1
endCode Breakdown
This template demonstrates the core DP pattern:
Key Variables:
prev2: Stores result fori-2prev1: Stores result fori-1current: Stores result for current positioni
Visual State Transition:
stateDiagram-v2
[*] --> Base: n <= 1
Base --> Loop: n > 1
Loop --> Compute: Calculate current
Compute --> Update: Update prev2, prev1
Update --> Loop: Continue loop
Loop --> [*]: i > n
Critical Sections:
- Initialization: Handle base cases (n ≤ 1)
- Loop: Iterate through all positions
- Transition: Apply recurrence relation
- Update: Shift variables for next iteration
House Robber Pattern
Description: Maximum money from robbing houses without robbing adjacent houses. This template is directly used in House Robber.
/**
* Maximum money from robbing houses without robbing adjacent houses
* @param {number[]} nums - House values
* @returns {number} Maximum money
*/
function rob(nums) {
if (!nums.length) return 0;
// Space optimized: only track two previous states
let prev2 = 0, prev1 = nums[0];
for (let i = 1; i < nums.length; i++) {
const current = Math.max(prev1, prev2 + nums[i]);
prev2 = prev1;
prev1 = current;
}
return prev1;
}def rob(nums):
"""
Maximum money from robbing houses without robbing adjacent houses
"""
if not nums:
return 0
# Space optimized: only track two previous states
prev2, prev1 = 0, nums[0]
for i in range(1, len(nums)):
current = max(prev1, prev2 + nums[i])
prev2, prev1 = prev1, current
return prev1public class Solution {
/**
* Maximum money from robbing houses without robbing adjacent houses
*/
public int rob(int[] nums) {
if (nums.length == 0) return 0;
// Space optimized: only track two previous states
int prev2 = 0, prev1 = nums[0];
for (int i = 1; i < nums.length; i++) {
int current = Math.max(prev1, prev2 + nums[i]);
prev2 = prev1;
prev1 = current;
}
return prev1;
}
}class Solution {
public:
/**
* Maximum money from robbing houses without robbing adjacent houses
*/
int rob(vector<int>& nums) {
if (nums.empty()) return 0;
// Space optimized: only track two previous states
int prev2 = 0, prev1 = nums[0];
for (int i = 1; i < nums.size(); i++) {
int current = max(prev1, prev2 + nums[i]);
prev2 = prev1;
prev1 = current;
}
return prev1;
}
};func rob(nums []int) int {
if len(nums) == 0 {
return 0
}
// Space optimized: only track two previous states
prev2, prev1 := 0, nums[0]
for i := 1; i < len(nums); i++ {
current := max(prev1, prev2+nums[i])
prev2, prev1 = prev1, current
}
return prev1
}
func max(a, b int) int {
if a > b {
return a
}
return b
}def rob(nums)
return 0 if nums.empty?
# Space optimized: only track two previous states
prev2, prev1 = 0, nums[0]
(1...nums.length).each do |i|
current = [prev1, prev2 + nums[i]].max
prev2, prev1 = prev1, current
end
prev1
end2D Matrix DP Templates
Longest Common Subsequence
Description: Find length of longest common subsequence between two strings.
/**
* Find length of longest common subsequence
* @param {string} text1 - First string
* @param {string} text2 - Second string
* @returns {number} LCS length
*/
function longestCommonSubsequence(text1, text2) {
const m = text1.length, n = text2.length;
const dp = Array(m + 1).fill().map(() => 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] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}def longest_common_subsequence(text1, text2):
"""
Find length of longest common subsequence
"""
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] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
return dp[m][n]public class Solution {
/**
* Find length of longest common subsequence
*/
public 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] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
}class Solution {
public:
/**
* Find length of longest common subsequence
*/
int longestCommonSubsequence(string text1, string text2) {
int m = text1.length(), n = text2.length();
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] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
};func longestCommonSubsequence(text1 string, text2 string) int {
m, n := len(text1), len(text2)
dp := make([][]int, m+1)
for i := range dp {
dp[i] = make([]int, n+1)
}
for i := 1; i <= m; i++ {
for j := 1; j <= n; j++ {
if text1[i-1] == text2[j-1] {
dp[i][j] = dp[i-1][j-1] + 1
} else {
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
}
}
}
return dp[m][n]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}def longest_common_subsequence(text1, text2)
m, n = text1.length, text2.length
dp = Array.new(m + 1) { Array.new(n + 1, 0) }
(1..m).each do |i|
(1..n).each do |j|
if text1[i - 1] == text2[j - 1]
dp[i][j] = dp[i - 1][j - 1] + 1
else
dp[i][j] = [dp[i - 1][j], dp[i][j - 1]].max
end
end
end
dp[m][n]
endEdit Distance (Levenshtein Distance)
Description: Minimum operations to convert word1 to word2 (insert, delete, replace).
/**
* Minimum operations to convert word1 to word2 (insert, delete, replace)
* @param {string} word1 - Source word
* @param {string} word2 - Target word
* @returns {number} Minimum operations
*/
function minDistance(word1, word2) {
const m = word1.length, n = word2.length;
const dp = Array(m + 1).fill().map(() => Array(n + 1).fill(0));
// Initialize base cases
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] = Math.min(
dp[i - 1][j] + 1, // Delete
dp[i][j - 1] + 1, // Insert
dp[i - 1][j - 1] + 1 // Replace
);
}
}
}
return dp[m][n];
}def min_distance(word1, word2):
"""
Minimum operations to convert word1 to word2 (insert, delete, replace)
"""
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
# Initialize base cases
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] = min(
dp[i - 1][j] + 1, # Delete
dp[i][j - 1] + 1, # Insert
dp[i - 1][j - 1] + 1 # Replace
)
return dp[m][n]public class Solution {
/**
* Minimum operations to convert word1 to word2 (insert, delete, replace)
*/
public int minDistance(String word1, String word2) {
int m = word1.length(), n = word2.length();
int[][] dp = new int[m + 1][n + 1];
// Initialize base cases
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] = Math.min(
Math.min(dp[i - 1][j] + 1, // Delete
dp[i][j - 1] + 1), // Insert
dp[i - 1][j - 1] + 1 // Replace
);
}
}
}
return dp[m][n];
}
}class Solution {
public:
/**
* Minimum operations to convert word1 to word2 (insert, delete, replace)
*/
int minDistance(string word1, string word2) {
int m = word1.length(), n = word2.length();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
// Initialize base cases
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] = min({
dp[i - 1][j] + 1, // Delete
dp[i][j - 1] + 1, // Insert
dp[i - 1][j - 1] + 1 // Replace
});
}
}
}
return dp[m][n];
}
};func minDistance(word1 string, word2 string) int {
m, n := len(word1), len(word2)
dp := make([][]int, m+1)
for i := range dp {
dp[i] = make([]int, n+1)
}
// Initialize base cases
for i := 0; i <= m; i++ {
dp[i][0] = i
}
for j := 0; j <= n; j++ {
dp[0][j] = j
}
for i := 1; i <= m; i++ {
for j := 1; j <= n; j++ {
if word1[i-1] == word2[j-1] {
dp[i][j] = dp[i-1][j-1]
} else {
dp[i][j] = min(
min(dp[i-1][j]+1, // Delete
dp[i][j-1]+1), // Insert
dp[i-1][j-1]+1) // Replace
}
}
}
return dp[m][n]
}
func min(a, b int) int {
if a < b {
return a
}
return b
}def min_distance(word1, word2)
m, n = word1.length, word2.length
dp = Array.new(m + 1) { Array.new(n + 1, 0) }
# Initialize base cases
(0..m).each { |i| dp[i][0] = i }
(0..n).each { |j| dp[0][j] = j }
(1..m).each do |i|
(1..n).each do |j|
if word1[i - 1] == word2[j - 1]
dp[i][j] = dp[i - 1][j - 1]
else
dp[i][j] = [
dp[i - 1][j] + 1, # Delete
dp[i][j - 1] + 1, # Insert
dp[i - 1][j - 1] + 1 # Replace
].min
end
end
end
dp[m][n]
endKnapsack DP Templates
0/1 Knapsack
Description: Each item can be used at most once.
/**
* 0/1 Knapsack: each item can be used at most once
* @param {number[]} weights - Item weights
* @param {number[]} values - Item values
* @param {number} capacity - Knapsack capacity
* @returns {number} Maximum value
*/
function knapsack01(weights, values, capacity) {
const n = weights.length;
const dp = Array(n + 1).fill().map(() => Array(capacity + 1).fill(0));
for (let i = 1; i <= n; i++) {
for (let w = 0; w <= capacity; w++) {
if (weights[i - 1] <= w) {
dp[i][w] = Math.max(
dp[i - 1][w], // Don't take
values[i - 1] + dp[i - 1][w - weights[i - 1]] // Take
);
} else {
dp[i][w] = dp[i - 1][w];
}
}
}
return dp[n][capacity];
}def knapsack_01(weights, values, capacity):
"""
0/1 Knapsack: each item can be used at most once
"""
n = len(weights)
dp = [[0] * (capacity + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for w in range(capacity + 1):
if weights[i - 1] <= w:
dp[i][w] = max(
dp[i - 1][w], # Don't take
values[i - 1] + dp[i - 1][w - weights[i - 1]] # Take
)
else:
dp[i][w] = dp[i - 1][w]
return dp[n][capacity]public class Solution {
/**
* 0/1 Knapsack: each item can be used at most once
*/
public int knapsack01(int[] weights, int[] values, int capacity) {
int n = weights.length;
int[][] dp = new int[n + 1][capacity + 1];
for (int i = 1; i <= n; i++) {
for (int w = 0; w <= capacity; w++) {
if (weights[i - 1] <= w) {
dp[i][w] = Math.max(
dp[i - 1][w], // Don't take
values[i - 1] + dp[i - 1][w - weights[i - 1]] // Take
);
} else {
dp[i][w] = dp[i - 1][w];
}
}
}
return dp[n][capacity];
}
}class Solution {
public:
/**
* 0/1 Knapsack: each item can be used at most once
*/
int knapsack01(vector<int>& weights, vector<int>& values, int capacity) {
int n = weights.size();
vector<vector<int>> dp(n + 1, vector<int>(capacity + 1, 0));
for (int i = 1; i <= n; i++) {
for (int w = 0; w <= capacity; w++) {
if (weights[i - 1] <= w) {
dp[i][w] = max(
dp[i - 1][w], // Don't take
values[i - 1] + dp[i - 1][w - weights[i - 1]] // Take
);
} else {
dp[i][w] = dp[i - 1][w];
}
}
}
return dp[n][capacity];
}
};func knapsack01(weights, values []int, capacity int) int {
n := len(weights)
dp := make([][]int, n+1)
for i := range dp {
dp[i] = make([]int, capacity+1)
}
for i := 1; i <= n; i++ {
for w := 0; w <= capacity; w++ {
if weights[i-1] <= w {
dp[i][w] = max(
dp[i-1][w], // Don't take
values[i-1] + dp[i-1][w-weights[i-1]] // Take
)
} else {
dp[i][w] = dp[i-1][w]
}
}
}
return dp[n][capacity]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}def knapsack_01(weights, values, capacity)
n = weights.length
dp = Array.new(n + 1) { Array.new(capacity + 1, 0) }
(1..n).each do |i|
(0..capacity).each do |w|
if weights[i - 1] <= w
dp[i][w] = [
dp[i - 1][w], # Don't take
values[i - 1] + dp[i - 1][w - weights[i - 1]] # Take
].max
else
dp[i][w] = dp[i - 1][w]
end
end
end
dp[n][capacity]
endUnbounded Knapsack (Coin Change)
Description: Each item can be used unlimited times.
/**
* Unbounded Knapsack: each item can be used unlimited times
* @param {number[]} coins - Available coin denominations
* @param {number} amount - Target amount
* @returns {number} Fewest coins needed, or -1 if impossible
*/
function coinChange(coins, amount) {
const dp = new Array(amount + 1).fill(amount + 1);
dp[0] = 0;
for (const coin of coins) {
for (let i = coin; i <= amount; i++) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
return dp[amount] === amount + 1 ? -1 : dp[amount];
}def coin_change(coins, amount):
"""
Unbounded Knapsack: each item can be used unlimited times
"""
dp = [amount + 1] * (amount + 1)
dp[0] = 0
for coin in coins:
for i in range(coin, amount + 1):
dp[i] = min(dp[i], dp[i - coin] + 1)
return -1 if dp[amount] == amount + 1 else dp[amount]public class Solution {
/**
* Unbounded Knapsack: each item can be used unlimited times
*/
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, amount + 1);
dp[0] = 0;
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
return dp[amount] == amount + 1 ? -1 : dp[amount];
}
}class Solution {
public:
/**
* Unbounded Knapsack: each item can be used unlimited times
*/
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1, amount + 1);
dp[0] = 0;
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
dp[i] = min(dp[i], dp[i - coin] + 1);
}
}
return dp[amount] == amount + 1 ? -1 : dp[amount];
}
};func coinChange(coins []int, amount int) int {
dp := make([]int, amount+1)
for i := range dp {
dp[i] = amount + 1
}
dp[0] = 0
for _, coin := range coins {
for i := coin; i <= amount; i++ {
dp[i] = min(dp[i], dp[i-coin]+1)
}
}
if dp[amount] == amount+1 {
return -1
}
return dp[amount]
}
func min(a, b int) int {
if a < b {
return a
}
return b
}def coin_change(coins, amount)
dp = Array.new(amount + 1, amount + 1)
dp[0] = 0
coins.each do |coin|
(coin..amount).each do |i|
dp[i] = [dp[i], dp[i - coin] + 1].min
end
end
dp[amount] == amount + 1 ? -1 : dp[amount]
endPractice Tips
Choosing Between Approaches
- Memoization: Natural recursive structure, solves subproblems on demand
- Tabulation: Space efficient, processes in logical order
- Space Optimization: Reduce O(n²) to O(n) or O(k) when possible
Common Patterns
- 1D Array: Linear sequences (house robber, climb stairs)
- 2D Matrix: String comparisons, LCS, edit distance
- Knapsack: Optimization with constraints
- Interval DP: Matrix chain multiplication, palindrome partitioning
Debugging Templates
// Print DP table
function printDP(dp) {
for (const row of dp) {
console.log(row.join(' '));
}
}
// Verify DP transitions
function verifyDP(i, j, expected, dp) {
const actual = dp[i][j];
console.log(`DP[${i}][${j}]: expected ${expected}, got ${actual}`);
}