Dynamic Programming II: Classic Problems

DP — LIS, LCS, Knapsack, Edit Distance

Read: ~55 min Advanced 8 practice problems Live code runner

1. The Five Classics

  • LIS — Longest Increasing Subsequence
  • LCS — Longest Common Subsequence
  • Edit Distance — minimum edits to transform one string into another
  • Knapsack — pack maximum value under weight limit
  • Matrix Chain Multiplication — minimum scalar multiplications

Every interview, every contest — these five (or close cousins) appear constantly. Drilling them gives a vocabulary you can transfer to new DP problems instantly.

পাঁচটি classic DP — যেকোনো ইন্টারভিউ বা কনটেস্টে এগুলোর কোনো না কোনো রূপ আসেই। মুখস্থ template রাখলে ৫ মিনিটেই solve হয়ে যায়।

2. LIS in O(n log n)

Maintain tails: tails[k] = smallest possible tail of any increasing subsequence of length k+1 seen so far. For each new x, binary-search for the first tails[i] ≥ x and replace it. Length of tails at the end = LIS length.

lis.cpp
#include <bits/stdc++.h>
using namespace std;

int LIS(vector<int>& a) {
    vector<int> tails;
    for (int x : a) {
        auto it = lower_bound(tails.begin(), tails.end(), x);
        if (it == tails.end()) tails.push_back(x);
        else *it = x;
    }
    return tails.size();
}

int main() {
    vector<int> a = {10, 9, 2, 5, 3, 7, 101, 18};
    cout << "LIS = " << LIS(a);   // 4 (e.g. 2,3,7,101)
}
Caution tails is not a valid increasing subsequence — only its length is. To recover the actual subsequence, store predecessor pointers per index.

3. LCS & Edit Distance

LCS: dp[i][j] = LCS length of a[0..i−1] and b[0..j−1]. If a[i−1] == b[j−1]: dp[i][j] = dp[i−1][j−1] + 1. Else dp[i][j] = max(dp[i−1][j], dp[i][j−1]). O(nm).

Edit Distance (Levenshtein): dp[i][j] = ops to convert a[0..i−1] into b[0..j−1]. Three operations (insert / delete / replace), each costs 1:

dp[i][j] = (a[i−1] == b[j−1]) ? dp[i−1][j−1] : 1 + min(dp[i−1][j], dp[i][j−1], dp[i−1][j−1])

lcs_edit.cpp
#include <bits/stdc++.h>
using namespace std;

int LCS(const string& a, const string& b) {
    int n = a.size(), m = b.size();
    vector<vector<int>> dp(n+1, vector<int>(m+1, 0));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            dp[i][j] = (a[i-1] == b[j-1]) ? dp[i-1][j-1] + 1
                                            : max(dp[i-1][j], dp[i][j-1]);
    return dp[n][m];
}

int edit(const string& a, const string& b) {
    int n = a.size(), m = b.size();
    vector<vector<int>> dp(n+1, vector<int>(m+1));
    for (int i = 0; i <= n; i++) dp[i][0] = i;
    for (int j = 0; j <= m; j++) dp[0][j] = j;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            dp[i][j] = (a[i-1] == b[j-1])
                ? dp[i-1][j-1]
                : 1 + min({dp[i-1][j], dp[i][j-1], dp[i-1][j-1]});
    return dp[n][m];
}

int main() {
    cout << "LCS  ABCBDAB / BDCAB = " << LCS("ABCBDAB", "BDCAB") << "\n";
    cout << "edit kitten / sitting = " << edit("kitten", "sitting");
}

4. Knapsack

0/1 Knapsack: n items, each with weight w[i] and value v[i]; pick subset with total weight ≤ W to maximise value. dp[i][w] = best value using first i items in weight w. Either skip i or take i: dp[i][w] = max(dp[i−1][w], dp[i−1][w − w[i]] + v[i]). O(nW). Space-optimised: 1D dp[w] updated in reverse order so we don't reuse the same item.

knapsack01.cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    vector<int> w = {2, 3, 4, 5};
    vector<int> v = {3, 4, 5, 6};
    int W = 5;
    vector<int> dp(W + 1, 0);
    for (int i = 0; i < (int)w.size(); i++)
        for (int j = W; j >= w[i]; j--)
            dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
    cout << "max value = " << dp[W];
}

Unbounded: each item can be taken any number of times → loop j forward instead.

5. Matrix Chain Multiplication

Given matrix dimensions p[0..n], find optimal parenthesisation. dp[i][j] = min scalar multiplications to multiply A[i..j]. dp[i][j] = min over k of dp[i][k] + dp[k+1][j] + p[i−1]·p[k]·p[j]. O(n³).

6. Practice Problems

  1. Longest common substring (contiguous) — different from LCS.
    Longest common substring।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: dp[i][j] = if a[i−1]==b[j−1] then dp[i−1][j−1] + 1 else 0. Answer = max over all dp[i][j].

  2. Coin change — minimum coins to sum to amount, given unlimited coin supply.
    Coin change minimum coins।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: dp[v] = min coins to form value v. dp[v] = min over coin c of dp[v − c] + 1. dp[0] = 0, others = INF. O(V·n).

  3. Coin change — number of ways to form amount (order doesn't matter).
    Coin change — total ways।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: dp[v] = ways to make v. Outer loop over coins, inner loop v from coin to amount: dp[v] += dp[v − coin]. Order coins outside avoids counting permutations.

  4. Subset sum — does any subset of nums sum to target?
    Subset sum।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: bool dp[w]; dp[0] = true; for each num x, for w from W downto x: dp[w] |= dp[w − x]. O(n·W).

  5. Partition equal subset sum — can the array be split into two halves with equal sum?
    Equal subset sum।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: if total sum is odd, impossible. Otherwise check subset sum target = total/2.

  6. Egg drop — min trials with k eggs and n floors.
    Egg drop।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: the smart trick: dp[t][k] = max floors we can certify in t trials with k eggs. dp[t][k] = dp[t−1][k−1] + dp[t−1][k] + 1. Find smallest t with dp[t][k] ≥ n.

  7. Print the actual LCS (not just length).
    LCS string পুনরুদ্ধার।
    ✨ Show Answer (উত্তর দেখুন)

    Approach: after the DP, walk from dp[n][m] back to dp[0][0]: if a[i−1] == b[j−1] take it and go to (i−1, j−1); else go to whichever neighbour matches dp[i][j].

  8. Why is the inner loop of 0/1 knapsack iterated backward?
    0/1 knapsack-এ inner loop reverse কেন?
    ✨ Show Answer (উত্তর দেখুন)

    Answer: reverse order ensures we use the previous row's dp[w − w[i]], not the current row's — preventing reuse of item i. Forward order would model unbounded knapsack instead.

Summary — Module 35

LIS in O(n log n) via patience sorting. LCS / edit distance in O(nm) — common subsequence templates. 0/1 knapsack with reverse loop to avoid reuse. Matrix chain in O(n³). These five power 80% of all DP interview questions.

পাঁচটি classic DP — LIS, LCS, edit distance, knapsack, matrix chain। সবগুলোর template মুখস্থ থাকুক।

Next Module → Dynamic Programming III: Bitmask, Digit, Tree DP।