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 and , 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:
- Characters are alphanumeric.
Approach & Intuition
Let denote the length of the LCS of prefixes and .
- If : The character must be part of an optimal extension: .
- If : The optimal subsequence cannot match both concurrently; it inherits the maximum from dropping either or : .
Mathematical Recurrence
Stepped Calculation Trace
Let () and ():
Tabulation Matrix:
| B | D | C | A | B | A | ||
|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
| A | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
| B | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
| C | 0 | 1 | 1 | 2 | 2 | 2 | 2 |
| B | 0 | 1 | 1 | 2 | 2 | 3 | 3 |
| D | 0 | 1 | 2 | 2 | 2 | 3 | 3 |
| A | 0 | 1 | 2 | 2 | 3 | 3 | 4 |
| B | 0 | 1 | 2 | 2 | 3 | 4 | 4 |
Backtracking Traceback for Sequence Reconstruction:
- From with : . Move left to .
- At with : Characters match! Select 'B'. Move diagonally to .
- At with : Characters match! Select 'A'. Move diagonally to .
- At with : . Move up to .
- At with : Move up to .
- At with : Characters match! Select 'C'. Move diagonally to .
- At with : Move left to .
- At with : Characters match! Select 'B'. Move diagonally to .
- Reconstructed LCS (reversed): "BCAB" (or "BDAB"), length = 4.
Complexity Analysis
- Time Complexity: to fill table, for reconstruction traceback.
- Space Complexity: for full table (can be reduced to if only LCS length is required).
◇Problem 2: Matrix Chain Multiplication (MCM)
Problem Description
Given a sequence of matrices , where matrix has dimension . Find the optimal parenthesization that minimizes the total number of scalar multiplications needed to compute the product .
Constraints:
Approach & Intuition: Interval DP
Multiplying two matrices of size and takes scalar multiplications.
Let be the minimum scalar multiplications needed to compute ().
For : (single matrix requires 0 multiplications).
For : We split the product into two subchains at some index ():
Stepped Calculation Trace
Let , corresponding to 3 matrices:
- Chain length :
- Chain length :
- , split .
- , split .
- Chain length ():
- Split : .
- Split : .
- Optimal: with optimal split , yielding .
◇Complete C++ Implementation
#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
- Previous Topic: ◇[PS] Emperor Arcana
- Next Topic: ◇[PS] Lovers Arcana
- Related Notes: ◇Dynamic Programming Introduction