PROJECTENGRAM

[PS] HIEROPHANT ARCANA

Hierophant Arcana: Longest Common Subsequence & Matrix Chain Multiplication

Tags: #◇CSALGCM #◇Problem Sets #◇HackerRank #◇Algorithms #◇Dynamic Programming


◇Overview

The Hierophant Arcana focuses on 2D table dynamic programming and interval dynamic programming. This set covers Longest Common Subsequence (LCS) (prefix-based string DP) and Matrix Chain Multiplication (MCM) (interval/subchain DP).


◇Problem 1: Longest Common Subsequence (LCS)

Problem Description

Given two sequences X=⟨x1,x2,…,xm⟩X = \langle x_1, x_2, \dots, x_m \rangle and Y=⟨y1,y2,…,yn⟩Y = \langle y_1, y_2, \dots, y_n \rangle, determine the length of their longest common subsequence (LCS) and reconstruct one such optimal sequence. A subsequence is derived by deleting zero or more elements without changing the order of remaining elements.

Constraints:

  • 1≤m,n≤10001 \le m, n \le 1000
  • Characters are alphanumeric.

Approach & Intuition

Let L[i][j]L[i][j] denote the length of the LCS of prefixes X[1…i]X[1 \dots i] and Y[1…j]Y[1 \dots j].

  1. If xi=yjx_i = y_j: The character must be part of an optimal extension: L[i][j]=L[i−1][j−1]+1L[i][j] = L[i-1][j-1] + 1.
  2. If xi≠yjx_i \ne y_j: The optimal subsequence cannot match both concurrently; it inherits the maximum from dropping either xix_i or yjy_j: L[i][j]=max⁡(L[i−1][j],L[i][j−1])L[i][j] = \max(L[i-1][j], L[i][j-1]).

Mathematical Recurrence

L[i][j]={0if i=0 or j=0L[i−1][j−1]+1if xi=yjmax⁡(L[i−1][j],L[i][j−1])if xi≠yjL[i][j] = \begin{cases} 0 & \text{if } i = 0 \text{ or } j = 0 \\ L[i-1][j-1] + 1 & \text{if } x_i = y_j \\ \max(L[i-1][j], L[i][j-1]) & \text{if } x_i \ne y_j \end{cases}

Stepped Calculation Trace

Let X="ABCBDAB"X = \text{"ABCBDAB"} (m=7m=7) and Y="BDCABA"Y = \text{"BDCABA"} (n=6n=6):

Tabulation Matrix: L[i][j]L[i][j]

X\YX \backslash Y∅\emptysetBDCABA
∅\emptyset0000000
A0000111
B0111122
C0112222
B0112233
D0122233
A0122334
B0122344

Backtracking Traceback for Sequence Reconstruction:

  • From (7,6)(7, 6) with X[7]=′B′,Y[6]=′A′X[7]='B', Y[6]='A': L[7][6]=4==L[7][5]=4L[7][6]=4 == L[7][5]=4. Move left to (7,5)(7, 5).
  • At (7,5)(7, 5) with X[7]=′B′,Y[5]=′B′X[7]='B', Y[5]='B': Characters match! Select 'B'. Move diagonally to (6,4)(6, 4).
  • At (6,4)(6, 4) with X[6]=′A′,Y[4]=′A′X[6]='A', Y[4]='A': Characters match! Select 'A'. Move diagonally to (5,3)(5, 3).
  • At (5,3)(5, 3) with X[5]=′D′,Y[3]=′C′X[5]='D', Y[3]='C': L[5][3]=2==L[4][3]=2L[5][3]=2 == L[4][3]=2. Move up to (4,3)(4, 3).
  • At (4,3)(4, 3) with X[4]=′B′,Y[3]=′C′X[4]='B', Y[3]='C': Move up to (3,3)(3, 3).
  • At (3,3)(3, 3) with X[3]=′C′,Y[3]=′C′X[3]='C', Y[3]='C': Characters match! Select 'C'. Move diagonally to (2,2)(2, 2).
  • At (2,2)(2, 2) with X[2]=′B′,Y[2]=′D′X[2]='B', Y[2]='D': Move left to (2,1)(2, 1).
  • At (2,1)(2, 1) with X[2]=′B′,Y[1]=′B′X[2]='B', Y[1]='B': Characters match! Select 'B'. Move diagonally to (1,0)(1, 0).
  • Reconstructed LCS (reversed): "BCAB" (or "BDAB"), length = 4.

Complexity Analysis

  • Time Complexity: O(m⋅n)O(m \cdot n) to fill table, O(m+n)O(m + n) for reconstruction traceback.
  • Space Complexity: O(m⋅n)O(m \cdot n) for full table (can be reduced to O(min⁡(m,n))O(\min(m, n)) if only LCS length is required).

◇Problem 2: Matrix Chain Multiplication (MCM)

Problem Description

Given a sequence of nn matrices ⟨A1,A2,…,An⟩\langle A_1, A_2, \dots, A_n \rangle, where matrix AiA_i has dimension pi−1×pip_{i-1} \times p_i. Find the optimal parenthesization that minimizes the total number of scalar multiplications needed to compute the product A1A2…AnA_1 A_2 \dots A_n.

Constraints:

  • 2≤n≤1002 \le n \le 100
  • 1≤pi≤5001 \le p_i \le 500

Approach & Intuition: Interval DP

Multiplying two matrices of size p×qp \times q and q×rq \times r takes p⋅q⋅rp \cdot q \cdot r scalar multiplications.
Let m[i][j]m[i][j] be the minimum scalar multiplications needed to compute Ai…AjA_i \dots A_j (1≤i≤j≤n1 \le i \le j \le n).
For i=ji = j: m[i][i]=0m[i][i] = 0 (single matrix requires 0 multiplications).
For i<ji < j: We split the product into two subchains (Ai…Ak)(Ak+1…Aj)(A_i \dots A_k)(A_{k+1} \dots A_j) at some index kk (i≤k<ji \le k < j):
m[i][j]=min⁡i≤k<j(m[i][k]+m[k+1][j]+pi−1pkpj)m[i][j] = \min_{i \le k < j} \left( m[i][k] + m[k+1][j] + p_{i-1} p_k p_j \right)

Stepped Calculation Trace

Let p=[10,30,5,60]p = [10, 30, 5, 60], corresponding to 3 matrices:

  • A1:10×30A_1: 10 \times 30
  • A2:30×5A_2: 30 \times 5
  • A3:5×60A_3: 5 \times 60
  1. Chain length L=1L = 1:
    • m[1][1]=0,m[2][2]=0,m[3][3]=0m[1][1] = 0, \quad m[2][2] = 0, \quad m[3][3] = 0
  2. Chain length L=2L = 2:
    • m[1][2]=m[1][1]+m[2][2]+p0p1p2=0+0+(10×30×5)=1500m[1][2] = m[1][1] + m[2][2] + p_0 p_1 p_2 = 0 + 0 + (10 \times 30 \times 5) = 1500, split s[1][2]=1s[1][2] = 1.
    • m[2][3]=m[2][2]+m[3][3]+p1p2p3=0+0+(30×5×60)=9000m[2][3] = m[2][2] + m[3][3] + p_1 p_2 p_3 = 0 + 0 + (30 \times 5 \times 60) = 9000, split s[2][3]=2s[2][3] = 2.
  3. Chain length L=3L = 3 (m[1][3]m[1][3]):
    • Split k=1k = 1: (A1)(A2A3)  ⟹  m[1][1]+m[2][3]+p0p1p3=0+9000+(10×30×60)=9000+18000=27000(A_1)(A_2 A_3) \implies m[1][1] + m[2][3] + p_0 p_1 p_3 = 0 + 9000 + (10 \times 30 \times 60) = 9000 + 18000 = 27000.
    • Split k=2k = 2: (A1A2)(A3)  ⟹  m[1][2]+m[3][3]+p0p2p3=1500+0+(10×5×60)=1500+3000=4500(A_1 A_2)(A_3) \implies m[1][2] + m[3][3] + p_0 p_2 p_3 = 1500 + 0 + (10 \times 5 \times 60) = 1500 + 3000 = \mathbf{4500}.
    • Optimal: m[1][3]=4500m[1][3] = \mathbf{4500} with optimal split s[1][3]=2s[1][3] = 2, yielding ((A1A2)A3)((A_1 A_2) A_3).

◇Complete C++ Implementation

『』
CPP
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

// Problem 1: Longest Common Subsequence with Full Backtracking
pair<int, string> solveLCS(const string& X, const string& Y) {
    int m = X.length();
    int n = Y.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 (X[i - 1] == Y[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }

    // Reconstruction
    string lcs = "";
    int i = m, j = n;
    while (i > 0 && j > 0) {
        if (X[i - 1] == Y[j - 1]) {
            lcs += X[i - 1];
            i--; j--;
        } else if (dp[i - 1][j] >= dp[i][j - 1]) {
            i--;
        } else {
            j--;
        }
    }
    reverse(lcs.begin(), lcs.end());
    return {dp[m][n], lcs};
}

// Problem 2: Matrix Chain Multiplication
void printParenthesis(int i, int j, const vector<vector<int>>& s) {
    if (i == j) {
        cout << "A" << i;
        return;
    }
    cout << "(";
    printParenthesis(i, s[i][j], s);
    printParenthesis(s[i][j] + 1, j, s);
    cout << ")";
}

int solveMCM(const vector<int>& p) {
    int n = p.size() - 1; // Number of matrices
    vector<vector<int>> m(n + 1, vector<int>(n + 1, 0));
    vector<vector<int>> s(n + 1, vector<int>(n + 1, 0));

    // L is chain length
    for (int L = 2; L <= n; L++) {
        for (int i = 1; i <= n - L + 1; i++) {
            int j = i + L - 1;
            m[i][j] = 1e9;
            for (int k = i; k < j; k++) {
                int cost = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
                if (cost < m[i][j]) {
                    m[i][j] = cost;
                    s[i][j] = k;
                }
            }
        }
    }

    cout << "Optimal Parenthesization: ";
    printParenthesis(1, n, s);
    cout << "\n";

    return m[1][n];
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    // Test LCS
    string s1 = "ABCBDAB";
    string s2 = "BDCABA";
    auto [len, lcsStr] = solveLCS(s1, s2);
    cout << "LCS Length: " << len << ", String: " << lcsStr << "\n";

    // Test MCM
    vector<int> p = {10, 30, 5, 60};
    int minMults = solveMCM(p);
    cout << "Min Scalar Multiplications: " << minMults << "\n"; // Output: 4500

    return 0;
}

◇References & Navigation

[PS] Hierophant Arcana | PROJECT ENGRAM