Welcome to the first lesson of our sixth module, Dynamic Programming and Greedy Algorithms.
In our previous module, we concluded with Kruskal's and Prim's algorithms for finding a Minimum Spanning Tree. These are classic examples of greedy algorithms—they make the best-looking choice at each step without ever looking back. Today, we introduce a different, more powerful algorithmic paradigm: Dynamic Programming (DP). While greedy algorithms are fast and simple, they only work for a specific class of problems. DP can solve a much broader range of complex optimization problems, making it one of the most important topics for elite technical interviews.
Your goal for this lesson is to learn how to identify when Dynamic Programming is the right tool for the job. Specifically, you will learn to spot the two fundamental properties that define a DP problem: optimal substructure and overlapping subproblems.
The Five Concepts for Today
- What is Dynamic Programming?: The core idea and how it differs from Greedy and Divide & Conquer.
- Overlapping Subproblems: The property that makes DP efficient.
- Optimal Substructure: The property that makes DP correct.
- The DP Litmus Test: Combining both properties to identify a DP problem.
- Interview Heuristics: Practical patterns for spotting DP in interview questions.
Concept 1: What is Dynamic Programming? A Change in Mindset
Technical Explanation
Dynamic Programming is an algorithmic technique for solving complex problems by breaking them down into simpler, smaller subproblems. The key idea is to solve each subproblem only once and store its solution. When the same subproblem is encountered again, we simply look up the stored result instead of re-computing it.
This technique is a form of "smart recursion." It's particularly effective for optimization problems (finding a minimum or maximum value) and counting problems (finding the number of ways to do something).
It's crucial to distinguish DP from other paradigms:
- Divide and Conquer: Also breaks problems into subproblems, but the subproblems are disjoint (independent). For example, in Merge Sort, sorting the left half of an array and the right half are completely separate tasks.
- Greedy Algorithms: Make a locally optimal choice at each step, hoping to find a global optimum. They never reconsider their choices. DP, by contrast, explores all choices at each step to guarantee a globally optimal solution.
The essence of DP is trading space for time. We use extra memory (a cache, table, or array) to store subproblem results, which dramatically reduces computation time from exponential to polynomial.
These notes from a practitioner's GitHub repository offer a fantastic high-level introduction to the philosophy behind DP. It explains why DP is a "superpower" for turning hard problems into manageable ones.
Read the introductory section titled 'Dynamic Programming.' Focus on the distinction between a problem being complicated versus having repetitive steps, and how DP addresses the latter.
Metaphor: The Meticulous Engineer
Imagine two engineers tasked with calculating a series of complex physics formulas.
- The "Naive Recursive" Engineer starts each new formula from scratch. If a formula requires the value of , they calculate it. If the next formula also needs , they calculate it again. And again.
- The "Dynamic Programming" Engineer is more methodical. The first time they calculate , they write the result down in their engineering notebook. From then on, whenever a formula needs , they just flip to the right page and look it up.
DP is the art of being the second engineer: do the work once, remember the result, and reuse it freely.
Concept 2: Overlapping Subproblems - The "Why It's Worth It" Property
Technical Explanation
A problem is said to have overlapping subproblems if a recursive algorithm solves the same subproblems repeatedly rather than generating new subproblems at each step. The "overlap" refers to these shared, recurring subproblems.
The classic example is calculating the -th Fibonacci number. The mathematical definition is . A naive recursive implementation directly translates this:
def naive_fib(n):
"""
Calculates the n-th Fibonacci number using naive recursion.
WARNING: This is extremely inefficient.
"""
if n <= 1:
return n
# The function calls itself twice, leading to redundant work.
return naive_fib(n - 1) + naive_fib(n - 2)
# To see the inefficiency, let's trace naive_fib(5):
# naive_fib(5)
# -> naive_fib(4)
# -> naive_fib(3)
# -> naive_fib(2) -> naive_fib(1) + naive_fib(0)
# -> naive_fib(1)
# -> naive_fib(2) -> naive_fib(1) + naive_fib(0) <-- Redundant call
# -> naive_fib(3) <-- Redundant call
# -> naive_fib(2) -> naive_fib(1) + naive_fib(0) <-- Redundant call
# -> naive_fib(1)
print(f"naive_fib(10) = {naive_fib(10)}")
# Try a larger number like 40 and see how long it takes! (Be prepared to stop it)
The time complexity of this approach is exponential, roughly , because each call generates two more calls, creating a massive tree of computations where entire branches are identical. This is the inefficiency that DP aims to eliminate. If subproblems didn't overlap, there would be no re-computation to avoid, and DP would offer no benefit over simple recursion.
Dynamic Programming | Set 1 (Overlapping Subproblems Property) | GeeksforGeeks
This GeeksforGeeks video provides a clear, visual explanation of the Fibonacci sequence, showing the recursion tree and explicitly highlighting the redundant computations that characterize overlapping subproblems.
Watch from the beginning to 03:15. Focus on how the recursion tree for fib(5) is drawn and how the video points out that fib(3) and fib(2) are calculated multiple times. This visualizes the exponential work.
Metaphor: The Lego Sub-Assembly
Imagine building a large Lego spaceship. The instruction manual repeatedly says, "Build a laser cannon sub-assembly."
- A naive approach would be to get the individual bricks and build a new laser cannon from scratch every time you see this instruction.
- The DP approach is to build the first laser cannon, realize it's a common component, and then whenever the instructions ask for another one, you just grab the pre-built cannon you already made.
The overlapping subproblem is the "laser cannon sub-assembly."
Concept 3: Optimal Substructure - The "How It Works" Property
Technical Explanation
A problem exhibits optimal substructure if its overall optimal solution can be constructed from the optimal solutions to its subproblems. This property is a guarantee that it's safe to solve the smaller pieces perfectly and trust that they will combine into a perfect whole.
This property is shared with greedy algorithms, but the application differs. In DP, we explore combinations of optimal subproblem solutions, whereas greedy algorithms commit to just one.
Classic Example: Shortest Path in a Graph
If the shortest path from a starting city (A) to a destination city (C) passes through an intermediate city (B), then the path from A to B must be the shortest path between A and B, and the path from B to C must be the shortest path between B and C. If a shorter path existed from A to B, you could substitute it into the overall path to create a shorter A-to-C route, which contradicts the initial assumption that you had the shortest overall path.
Counter-Example: Longest Simple Path in a Graph
Finding the longest path between two vertices without repeating vertices does not have optimal substructure. The longest simple path from A to C might not be composed of the longest simple paths from A to B and B to C. Why? Because the longest path from A to B might use up vertices that are essential for creating the longest path from B to C, making the two sub-solutions incompatible.
Dynamic Programming (DP) Introduction
For a formal definition, the GeeksforGeeks article on DP provides a concise explanation of optimal substructure with the same shortest path example.
Read the section 'When to Use Dynamic Programming (DP)?', focusing on Part 1, 'Optimal Substructure.'
Metaphor: The Cross-Country Road Trip
You want to find the fastest driving route from New York City to Los Angeles. You determine that the fastest route goes through Chicago. The optimal substructure property means that the segment of your trip from NYC to Chicago must also be the absolute fastest route between those two cities. If it weren't, you could find a faster way to get to Chicago, which would in turn make your total trip to LA faster—a contradiction. You can trust that optimizing each leg of the journey contributes to optimizing the whole journey.
Concept 4: The DP Litmus Test: Combining the Properties
A problem is a candidate for a Dynamic Programming solution if and only if it exhibits both optimal substructure and overlapping subproblems.
- Optimal Substructure tells you that you can build the final solution from the solutions of subproblems (DP is a valid approach).
- Overlapping Subproblems tells you that you should use DP because you'll gain a massive efficiency boost by avoiding redundant work (DP is a worthwhile approach).

Let's apply this test to a common interview problem:
Problem: Coin Change
Given a set of coin denominations (e.g., [1, 5, 10]) and a target amount of money, find the minimum number of coins required to make that amount.
1. Does it have Optimal Substructure?
Let min_coins(A) be the minimum coins for amount A. To find min_coins(11) with coins [1, 5, 10], we can make a final move by using a 1-coin, a 5-coin, or a 10-coin.
- If we use a 1-coin, the total coins will be
1 + min_coins(10). - If we use a 5-coin, the total coins will be
1 + min_coins(6). - If we use a 10-coin, the total coins will be
1 + min_coins(1).
The optimal solution formin_coins(11)is the minimum of these three possibilities. We are building the optimal solution for 11 by relying on the optimal solutions for smaller amounts (10, 6, and 1). Yes, it has optimal substructure.
2. Does it have Overlapping Subproblems?
Let's trace the dependencies:
min_coins(11)needsmin_coins(10),min_coins(6),min_coins(1).min_coins(10)needsmin_coins(9),min_coins(5),min_coins(0).min_coins(9)needsmin_coins(8),min_coins(4).min_coins(6)needsmin_coins(5),min_coins(1).
Notice that bothmin_coins(11)(via its subproblemmin_coins(6)) andmin_coins(10)require the solution tomin_coins(5). We've found an overlapping subproblem. Yes, it has overlapping subproblems.
Conclusion: Since it has both properties, Coin Change is a classic DP problem.
Concept 5: Interview Heuristics - Spotting DP Patterns
In an interview, you won't always have time for a formal proof. You need to develop an intuition for recognizing DP problems quickly. Here are some strong signals:
- The question asks for a
min/maxvalue or thenumber of waysto do something.- Example: "What is the minimum cost..." or "How many ways are there to..." or "What is the longest possible subsequence..."
- Decisions made at one stage affect future decisions.
- Example: In the Knapsack problem, choosing to take an item reduces the weight capacity for all future choices.
- The problem involves sequences, strings, or grids, often dealing with prefixes, suffixes, or contiguous subarrays/subsequences.
- Example: Longest Common Subsequence, Longest Palindromic Substring.
- You can define a "state" with a few integer parameters.
- Example: For Fibonacci, the state is just
n. For Coin Change, it's theamount. For Knapsack, it might be(currentItemIndex, remainingCapacity).
- Example: For Fibonacci, the state is just
The 'Practical Considerations' section of the GitHub notes provides an excellent 'pattern-matching toolkit' for identifying DP problems in the wild.
Read the subsection 'Identifying DP Problems.' This provides a list of clues to look for in a problem description. Treat this as a checklist for your interview preparation.
Real-World DP Examples:
- Google Maps/Routing: Algorithms like Floyd-Warshall for all-pairs shortest paths are DP-based.
- Bioinformatics: The Smith-Waterman algorithm for DNA sequence alignment is a cornerstone of the field and is pure DP.
- Netflix/Spotify: A recommendation system might use DP to determine the optimal sequence of items to show a user.
Test your understanding!
You are given a string s. Your task is to find the length of the longest palindromic subsequence. A subsequence is formed by deleting zero or more characters from the string. A palindrome reads the same forwards and backwards.
Example: For input s = "bbbab", the longest palindromic subsequence is "bbbb", with length 4.
Use the DP litmus test. Does this problem have optimal substructure and overlapping subproblems?
Show answer
Let's analyze it. Let LPS(i, j) be the length of the longest palindromic subsequence in the substring s[i...j].
-
Optimal Substructure:
- If the characters at the ends match (
s[i] == s[j]), the LPS must include these two characters. The total length will be2 + LPS(i+1, j-1). We are building the solution based on the optimal solution of a smaller subproblem. - If the characters don't match (
s[i] != s[j]), the LPS is the better of two possibilities: either the LPS ofs[i+1...j]or the LPS ofs[i...j-1]. We must take the maximum:max(LPS(i+1, j), LPS(i, j-1)). - In both cases, we construct the solution from optimal solutions to subproblems. Yes, it has optimal substructure.
- If the characters at the ends match (
-
Overlapping Subproblems:
- To compute
LPS(i, j), we might needLPS(i+1, j)andLPS(i, j-1). LPS(i+1, j)in turn needsLPS(i+2, j)andLPS(i+1, j-1).LPS(i, j-1)in turn needsLPS(i+1, j-1)andLPS(i, j-2).- The subproblem
LPS(i+1, j-1)is required by both branches. Yes, it has overlapping subproblems.
- To compute
Conclusion: This is a classic DP problem.
Conclusion
Today, you've learned the fundamental theory behind Dynamic Programming, focusing on the two critical properties that make it both possible and powerful.
Key Takeaways:
- Dynamic Programming is a technique for solving complex problems by breaking them down and storing the results of subproblems to avoid re-computation.
- Overlapping Subproblems is the property of a problem where the same subproblems are solved multiple times, creating an opportunity for optimization.
- Optimal Substructure is the property where the optimal solution to the overall problem can be built from the optimal solutions of its subproblems.
- A problem is a prime candidate for DP if it has both of these properties.
- Heuristics like looking for
min/maxornumber of waysquestions can help you quickly identify potential DP problems in interviews.
Preview of the Next Lesson:
Now that you can identify a DP problem, the next step is to solve it. In our next lesson, we will explore the two primary implementation strategies for Dynamic Programming: Top-Down (Memoization) and Bottom-Up (Tabulation). We will take the concepts from today and turn them into working, efficient Python code.