LIS, Edit Distance and Matrix Chain DP

Classic DP problems for GATE and interviews: longest increasing subsequence, edit distance, matrix chain multiplication, subset sum and rod cutting, with recurrences, tables and code.

The first DP lesson introduced memoisation, coin change, LCS and 0/1 knapsack. This lesson adds five more classic problems. For each, the work is the same: define the subproblem, write the recurrence, fill the table in the right order.

ProblemDefine dp[state] in wordsWrite the recurrence from smaller statesSet the base casesChoose an order so smaller states come firstRead the answer from the tableDonegateshala.com
Designing a DP solution

Longest increasing subsequence (LIS)

best[i] = length of the longest strictly increasing subsequence ending at a[i]:

best[i] = 1 + max(best[j]) over j < i with a[j] < a[i] (or 1 if none). Answer = max best[i].

O(n²). A cleverer method keeps the smallest tail of each length and uses binary search: O(n log n).

Edit distance (Levenshtein)

Fewest insertions, deletions and replacements to turn s into t. d[i][j] = edit distance between the first i characters of s and the first j of t:

  • d[i][0] = i, d[0][j] = j
  • d[i][j] = min(d[i−1][j] + 1 (delete), d[i][j−1] + 1 (insert), d[i−1][j−1] + [s_i ≠ t_j] (replace or match))

O(mn) time. Spell checkers and DNA alignment use it. kitten → sitting needs 3 edits (k→s, e→i, insert g).

Matrix chain multiplication

Multiplying a p × q matrix by a q × r matrix takes pqr scalar multiplications. For a chain A₁A₂…Aₙ (Aᵢ is p[i−1] × p[i]), the order of multiplication changes the cost a lot.

m[i][j] = least cost to multiply Aᵢ…Aⱼ:

m[i][j] = min over i ≤ k < j of ( m[i][k] + m[k+1][j] + p[i−1]·p[k]·p[j] ), with m[i][i] = 0.

Fill by increasing chain length: O(n³) time, O(n²) space. For dimensions 10 × 30, 30 × 5, 5 × 60: (A₁A₂)A₃ costs 1500 + 3000 = 4500, but A₁(A₂A₃) costs 9000 + 18000 = 27000.

Subset sum

Is there a subset with sum exactly S? can[s] = some subset of the items seen so far sums to s. For each item v, update s from S down to v: can[s] |= can[s − v] (going down uses each item at most once). O(nS) — pseudo-polynomial, since S can be exponential in the input size (the problem is NP-complete).

Rod cutting

A rod of length n and price[i] for a piece of length i: best[n] = max over i of (price[i] + best[n − i]). This is an unbounded knapsack: a length can be used many times.

The code

public class Main {
    static int lis(int[] a) {                      // O(n^2): best[i] = LIS ending at i
        int[] best = new int[a.length];
        int ans = 0;
        for (int i = 0; i < a.length; i++) {
            best[i] = 1;
            for (int j = 0; j < i; j++)
                if (a[j] < a[i]) best[i] = Math.max(best[i], best[j] + 1);
            ans = Math.max(ans, best[i]);
        }
        return ans;
    }

    static int editDistance(String s, String t) {
        int[][] d = new int[s.length() + 1][t.length() + 1];
        for (int i = 0; i <= s.length(); i++) d[i][0] = i;     // delete everything
        for (int j = 0; j <= t.length(); j++) d[0][j] = j;     // insert everything
        for (int i = 1; i <= s.length(); i++)
            for (int j = 1; j <= t.length(); j++) {
                int replace = d[i - 1][j - 1] + (s.charAt(i - 1) == t.charAt(j - 1) ? 0 : 1);
                d[i][j] = Math.min(replace, Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1));
            }
        return d[s.length()][t.length()];
    }

    static int matrixChain(int[] p) {             // matrix i is p[i-1] x p[i]
        int n = p.length - 1;
        int[][] m = new int[n + 1][n + 1];
        for (int len = 2; len <= n; len++)
            for (int i = 1; i + len - 1 <= n; i++) {
                int j = i + len - 1;
                m[i][j] = Integer.MAX_VALUE;
                for (int k = i; k < j; k++)         // split between k and k+1
                    m[i][j] = Math.min(m[i][j], m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]);
            }
        return m[1][n];
    }

    static boolean subsetSum(int[] a, int target) {
        boolean[] can = new boolean[target + 1];
        can[0] = true;
        for (int v : a)
            for (int s = target; s >= v; s--)       // go downwards: each item used once
                can[s] = can[s] || can[s - v];
        return can[target];
    }

    static int rodCutting(int[] price, int n) {    // price[i] = price of a piece of length i
        int[] best = new int[n + 1];
        for (int len = 1; len <= n; len++)
            for (int cut = 1; cut <= len; cut++)
                best[len] = Math.max(best[len], price[cut] + best[len - cut]);
        return best[n];
    }

    public static void main(String[] args) {
        System.out.println("LIS of [10, 9, 2, 5, 3, 7, 101, 18] = " + lis(new int[]{10, 9, 2, 5, 3, 7, 101, 18}));
        System.out.println("edit distance(kitten, sitting) = " + editDistance("kitten", "sitting"));
        System.out.println("matrix chain [10, 30, 5, 60] = " + matrixChain(new int[]{10, 30, 5, 60}));
        System.out.println("subset of [3, 34, 4, 12, 5, 2] with sum 9? " + subsetSum(new int[]{3, 34, 4, 12, 5, 2}, 9));
        System.out.println("rod of length 8 = " + rodCutting(new int[]{0, 1, 5, 8, 9, 10, 17, 17, 20}, 8));
    }
}
Output
LIS of [10, 9, 2, 5, 3, 7, 101, 18] = 4
edit distance(kitten, sitting) = 3
matrix chain [10, 30, 5, 60] = 4500
subset of [3, 34, 4, 12, 5, 2] with sum 9? true
rod of length 8 = 22

GATE-style practice questions

GATE-styleNAT · numerical answer

Matrices A (10 × 20), B (20 × 5) and C (5 × 30) are multiplied. What is the minimum number of scalar multiplications?

Show answer and explanationAnswer: 2500

(AB)C = 1000 + 1500 = 2500, while A(BC) = 3000 + 6000 = 9000.

GATE-styleNAT · numerical answer

What is the edit distance between "sunday" and "saturday"?

Show answer and explanationAnswer: 3

Insert "a" and "t" after "s", and replace "n" with "r": 3 edits.

GATE-styleNAT · numerical answer

What is the length of the longest strictly increasing subsequence of [3, 10, 2, 1, 20, 4, 6, 7]?

Show answer and explanationAnswer: 4

For example 3, 4, 6, 7 (or 2, 4, 6, 7).

GATE-styleNAT · numerical answer

How many subproblems m[i][j] with i < j does matrix chain multiplication fill for a chain of 6 matrices?

Show answer and explanationAnswer: 15

One entry for every pair i < j: C(6, 2) = 15 (plus 6 zero entries on the diagonal).

GATE-styleMCQ · one correct answer

The subset-sum DP runs in O(nS) time. Why is subset sum still NP-complete?

Show answer and explanationAnswer: A

O(nS) is pseudo-polynomial: polynomial in the value S, not in its length log S.