Advanced Graphs, Strings & Dynamic Programming
Tarjan's and Kosaraju's Strongly Connected Components (SCC), Bridges & Articulation Points, KMP string prefix function, and Bitmask DP.
Reinforce your mental model: run, pause, and inspect live pointers with our interactive visualizer:
Advanced algorithmic paradigms conquer combinatorial explosions and topological complexities by exploiting hidden structural symmetries. From finding strongly connected components and linear pattern matching to flattening tree hierarchies and bitmask dynamic programming, these techniques power compilers, network routers, and high-performance search engines.
1. Executive Summary & Learning Objectives
#This module explores advanced computational techniques across graphs, strings, trees, and exponential state spaces, establishing rigorous mathematical invariants for industrial and competitive applications.
By the end of this chapter, you will be able to:
- Partition Directed & Undirected Graphs: Compute Strongly Connected Components via Kosaraju's two-pass algorithm and detect critical bridges using Tarjan's low-link timestamps in time.
- Execute Linear String Matching: Construct the KMP prefix-function ( table) in time and stream text searches in time without pointer backtracking.
- Flatten Tree Hierarchies: Apply the Euler Tour Technique to map subtree queries directly to contiguous 1D ranges .
- Compute Lowest Common Ancestors: Implement binary lifting via dynamic programming to jump ancestral powers of two in query time.
- Formulate Bitmask State Spaces: Compress subset membership into integer bitmasks to solve permutation-hard problems such as TSP in time.
2. Visual Architecture: KMP Failure Automata & Binary Lifting Dyadic Jumps
#Advanced algorithms systematically convert exponential or multi-pass linear scans into deterministic single-pass transitions or logarithmic dyadic jumps.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 940 450" width="100%" height="auto" style="max-width: 100%; height: auto; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;">
<defs>
<filter id="advGlow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="3" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
<marker id="adv-arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 1 L 10 5 L 0 9 z" fill="#38bdf8" />
</marker>
<marker id="adv-arrow-fallback" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 1 L 10 5 L 0 9 z" fill="#f59e0b" />
</marker>
<marker id="adv-arrow-jump" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 1 L 10 5 L 0 9 z" fill="#a855f7" />
</marker>
</defs>
<!-- Background Panel -->
<rect width="940" height="450" rx="14" fill="#0f172a" stroke="#1e293b" stroke-width="1.5" />
<!-- SECTION 1: KMP AUTOMATON (Left Panel) -->
<rect x="20" y="20" width="440" height="410" rx="10" fill="#1e293b" fill-opacity="0.4" stroke="#334155" stroke-width="1" />
<text x="36" y="48" fill="#f8fafc" font-size="14" font-weight="700">1. KMP Prefix-Suffix Automaton (LPS)</text>
<text x="36" y="68" fill="#94a3b8" font-size="11">Pattern: "ababa" (States 0..5, Non-Rewinding Fallbacks)</text>
<!-- KMP States -->
<!-- State 0 -->
<circle cx="65" cy="140" r="18" fill="#0f172a" stroke="#38bdf8" stroke-width="2" />
<text x="65" y="145" text-anchor="middle" fill="#f8fafc" font-size="11" font-weight="600">0</text>
<text x="65" y="175" text-anchor="middle" fill="#64748b" font-size="10">π=0</text>
<!-- Edge 0->1 ('a') -->
<path d="M 83 140 L 127 140" stroke="#38bdf8" stroke-width="1.8" marker-end="url(#adv-arrow)" />
<text x="105" y="132" text-anchor="middle" fill="#38bdf8" font-size="11" font-weight="700">'a'</text>
<!-- State 1 -->
<circle cx="145" cy="140" r="18" fill="#0f172a" stroke="#38bdf8" stroke-width="2" />
<text x="145" y="145" text-anchor="middle" fill="#f8fafc" font-size="11" font-weight="600">1</text>
<text x="145" y="175" text-anchor="middle" fill="#64748b" font-size="10">π=0</text>
<!-- Edge 1->2 ('b') -->
<path d="M 163 140 L 207 140" stroke="#38bdf8" stroke-width="1.8" marker-end="url(#adv-arrow)" />
<text x="185" y="132" text-anchor="middle" fill="#38bdf8" font-size="11" font-weight="700">'b'</text>
<!-- State 2 -->
<circle cx="225" cy="140" r="18" fill="#0f172a" stroke="#38bdf8" stroke-width="2" />
<text x="225" y="145" text-anchor="middle" fill="#f8fafc" font-size="11" font-weight="600">2</text>
<text x="225" y="175" text-anchor="middle" fill="#64748b" font-size="10">π=1</text>
<!-- Edge 2->3 ('a') -->
<path d="M 243 140 L 287 140" stroke="#38bdf8" stroke-width="1.8" marker-end="url(#adv-arrow)" />
<text x="265" y="132" text-anchor="middle" fill="#38bdf8" font-size="11" font-weight="700">'a'</text>
<!-- State 3 -->
<circle cx="305" cy="140" r="18" fill="#0f172a" stroke="#38bdf8" stroke-width="2" />
<text x="305" y="145" text-anchor="middle" fill="#f8fafc" font-size="11" font-weight="600">3</text>
<text x="305" y="175" text-anchor="middle" fill="#64748b" font-size="10">π=2</text>
<!-- Edge 3->4 ('b') -->
<path d="M 323 140 L 367 140" stroke="#38bdf8" stroke-width="1.8" marker-end="url(#adv-arrow)" />
<text x="345" y="132" text-anchor="middle" fill="#38bdf8" font-size="11" font-weight="700">'b'</text>
<!-- State 4 -->
<circle cx="385" cy="140" r="18" fill="#0f172a" stroke="#38bdf8" stroke-width="2" />
<text x="385" y="145" text-anchor="middle" fill="#f8fafc" font-size="11" font-weight="600">4</text>
<text x="385" y="175" text-anchor="middle" fill="#64748b" font-size="10">π=3</text>
<!-- Fallback Edges (Curved below) -->
<!-- Mismatch at State 4 -> Fallback to State 2 (length 2 "ab") -->
<path d="M 380 160 C 350 220, 260 220, 230 162" fill="none" stroke="#f59e0b" stroke-width="1.8" stroke-dasharray="4 3" marker-end="url(#adv-arrow-fallback)" />
<text x="305" y="222" text-anchor="middle" fill="#fbbf24" font-size="10" font-weight="600">Fallback π[3]=2 ("ab")</text>
<!-- Mismatch at State 3 -> Fallback to State 1 (length 1 "a") -->
<path d="M 300 160 C 270 260, 180 260, 150 162" fill="none" stroke="#f59e0b" stroke-width="1.8" stroke-dasharray="4 3" marker-end="url(#adv-arrow-fallback)" />
<text x="225" y="270" text-anchor="middle" fill="#fbbf24" font-size="10" font-weight="600">Fallback π[2]=1 ("a")</text>
<!-- Mathematical Invariant Explanation Box -->
<rect x="36" y="300" width="408" height="110" rx="6" fill="#0f172a" stroke="#1e293b" stroke-width="1.2" />
<text x="48" y="324" fill="#38bdf8" font-size="11" font-weight="700">KMP Linear Time Invariant:</text>
<text x="48" y="344" fill="#cbd5e1" font-size="10">Text index i NEVER decrements (amortized O(1) per step).</text>
<text x="48" y="362" fill="#cbd5e1" font-size="10">On mismatch, pattern index j jumps to π[j-1].</text>
<text x="48" y="380" fill="#94a3b8" font-size="10">Prefix matches suffix, preserving past comparisons.</text>
<text x="48" y="398" fill="#10b981" font-size="10" font-weight="600">Total Worst-Case Time: O(n + m), Space: O(m)</text>
<!-- SECTION 2: BINARY LIFTING (Right Panel) -->
<rect x="480" y="20" width="440" height="410" rx="10" fill="#1e293b" fill-opacity="0.4" stroke="#334155" stroke-width="1" />
<text x="496" y="48" fill="#f8fafc" font-size="14" font-weight="700">2. Binary Lifting LCA (Dyadic Ancestors)</text>
<text x="496" y="68" fill="#94a3b8" font-size="11">Decomposing Path Distance into Powers of 2: 2^k</text>
<!-- Tree Structure -->
<!-- Root node 1 (Depth 0) -->
<circle cx="700" cy="110" r="16" fill="#064e3b" stroke="#10b981" stroke-width="2" />
<text x="700" y="114" text-anchor="middle" fill="#a7f3d0" font-size="11" font-weight="700">LCA</text>
<text x="645" y="114" fill="#6ee7b7" font-size="10">Depth 0</text>
<!-- Node 2 (Depth 1) -->
<circle cx="630" cy="180" r="15" fill="#0f172a" stroke="#64748b" stroke-width="1.5" />
<text x="630" y="184" text-anchor="middle" fill="#f8fafc" font-size="10">Node A</text>
<line x1="685" y1="120" x2="640" y2="170" stroke="#475569" stroke-width="1.5" />
<text x="560" y="184" fill="#64748b" font-size="10">Depth 1</text>
<!-- Node 3 (Depth 2) -->
<circle cx="600" cy="250" r="15" fill="#0f172a" stroke="#64748b" stroke-width="1.5" />
<text x="600" y="254" text-anchor="middle" fill="#f8fafc" font-size="10">Node B</text>
<line x1="625" y1="195" x2="605" y2="235" stroke="#475569" stroke-width="1.5" />
<text x="530" y="254" fill="#64748b" font-size="10">Depth 2</text>
<!-- Node 4 (Depth 3) -->
<circle cx="580" cy="320" r="15" fill="#0f172a" stroke="#64748b" stroke-width="1.5" />
<text x="580" y="324" text-anchor="middle" fill="#f8fafc" font-size="10">Node C</text>
<line x1="595" y1="265" x2="585" y2="305" stroke="#475569" stroke-width="1.5" />
<text x="510" y="324" fill="#64748b" font-size="10">Depth 3</text>
<!-- Target Node U (Depth 4) -->
<circle cx="560" cy="390" r="16" fill="#3b0764" stroke="#a855f7" stroke-width="2" filter="url(#advGlow)" />
<text x="560" y="394" text-anchor="middle" fill="#f3e8ff" font-size="11" font-weight="700">U</text>
<line x1="575" y1="335" x2="565" y2="375" stroke="#475569" stroke-width="1.5" />
<text x="490" y="394" fill="#c084fc" font-size="10">Depth 4</text>
<!-- Right branch: Target Node V (Depth 4) -->
<circle cx="770" cy="180" r="15" fill="#0f172a" stroke="#64748b" stroke-width="1.5" />
<line x1="715" y1="120" x2="760" y2="170" stroke="#475569" stroke-width="1.5" />
<circle cx="800" cy="250" r="15" fill="#0f172a" stroke="#64748b" stroke-width="1.5" />
<line x1="775" y1="195" x2="795" y2="235" stroke="#475569" stroke-width="1.5" />
<circle cx="820" cy="320" r="15" fill="#0f172a" stroke="#64748b" stroke-width="1.5" />
<line x1="805" y1="265" x2="815" y2="305" stroke="#475569" stroke-width="1.5" />
<circle cx="840" cy="390" r="16" fill="#3b0764" stroke="#a855f7" stroke-width="2" filter="url(#advGlow)" />
<text x="840" y="394" text-anchor="middle" fill="#f3e8ff" font-size="11" font-weight="700">V</text>
<line x1="825" y1="335" x2="835" y2="375" stroke="#475569" stroke-width="1.5" />
<!-- Binary Dyadic Jump Curved Arrows from U -->
<!-- 2^0 = 1 jump to C -->
<path d="M 545 385 C 530 355, 530 335, 563 320" fill="none" stroke="#a855f7" stroke-width="1.8" stroke-dasharray="3 2" marker-end="url(#adv-arrow-jump)" />
<text x="510" y="355" fill="#c084fc" font-size="9">2^0=1</text>
<!-- 2^1 = 2 jump to B -->
<path d="M 545 380 C 500 320, 500 270, 582 250" fill="none" stroke="#a855f7" stroke-width="2" marker-end="url(#adv-arrow-jump)" />
<text x="495" y="290" fill="#d8b4fe" font-size="9" font-weight="600">2^1=2</text>
<!-- 2^2 = 4 jump to LCA (Root) -->
<path d="M 545 375 C 440 250, 480 130, 680 110" fill="none" stroke="#10b981" stroke-width="2.5" marker-end="url(#adv-arrow-jump)" />
<text x="460" y="160" fill="#34d399" font-size="10" font-weight="700">2^2=4 (LCA)</text>
</svg>3. Topic 152: Advanced Graph Algorithms (SCC & Bridges)
#1. Strongly Connected Components (SCC)
#In a directed graph , a Strongly Connected Component (SCC) is a maximal set of vertices such that for every pair , there exists a directed path from to and from to .
Kosaraju's Two-Pass Algorithm
- First DFS Pass: Perform DFS on . Upon completing vertex exploration, push the vertex onto a finishing stack .
- Transpose Graph: Construct by reversing the orientation of every directed edge in .
- Second DFS Pass: Pop vertices sequentially from . If a popped vertex is unvisited in , initiate a DFS from it in . The resulting traversal tree constitutes an entire independent SCC.
- Time Complexity:
- Space Complexity: auxiliary storage
Modern C++20 Implementation: Kosaraju's Algorithm
#include <iostream>
#include <vector>
#include <stack>
class KosarajuSCC {
private:
int V;
std::vector<std::vector<int>> adj;
std::vector<std::vector<int>> revAdj;
void dfs1(int u, std::vector<bool>& visited, std::stack<int>& finishStack) {
visited[u] = true;
for (int v : adj[u]) {
if (!visited[v]) {
dfs1(v, visited, finishStack);
}
}
finishStack.push(u);
}
void dfs2(int u, std::vector<bool>& visited, std::vector<int>& currentSCC) {
visited[u] = true;
currentSCC.push_back(u);
for (int v : revAdj[u]) {
if (!visited[v]) {
dfs2(v, visited, currentSCC);
}
}
}
public:
explicit KosarajuSCC(int vertices) : V(vertices), adj(vertices), revAdj(vertices) {}
void addEdge(int u, int v) {
adj[u].push_back(v);
revAdj[v].push_back(u);
}
std::vector<std::vector<int>> computeSCCs() {
std::stack<int> finishStack;
std::vector<bool> visited(V, false);
for (int i = 0; i < V; ++i) {
if (!visited[i]) {
dfs1(i, visited, finishStack);
}
}
std::fill(visited.begin(), visited.end(), false);
std::vector<std::vector<int>> sccs;
while (!finishStack.empty()) {
int u = finishStack.top();
finishStack.pop();
if (!visited[u]) {
std::vector<int> currentSCC;
dfs2(u, visited, currentSCC);
sccs.push_back(std::move(currentSCC));
}
}
return sccs;
}
};2. Bridges (Critical Connections) in Undirected Graphs
#A Bridge is an edge whose deletion strictly increases the number of connected components in an undirected graph.
Tarjan's Bridge Invariant
Maintain two DFS timestamps for each vertex :
- : Discovery time of node in the DFS tree.
- : Lowest discovery time reachable from through its DFS subtree and at most one back-edge.
An edge is a Bridge if and only if:
Modern C++20 Implementation: Tarjan's Bridge & Articulation Point Finder
#include <iostream>
#include <vector>
#include <algorithm>
class TarjanBridges {
private:
int V;
int timer;
std::vector<std::vector<int>> adj;
std::vector<int> disc;
std::vector<int> low;
std::vector<std::pair<int, int>> bridges;
void dfs(int u, int parent) {
disc[u] = low[u] = ++timer;
for (int v : adj[u]) {
if (v == parent) continue; // Direct edge back to caller
if (disc[v] != 0) {
// Back-edge: update low-link with discovery time of ancestor
low[u] = std::min(low[u], disc[v]);
} else {
// Forward tree-edge
dfs(v, u);
low[u] = std::min(low[u], low[v]);
// Bridge condition: no back-edge can bypass u
if (low[v] > disc[u]) {
bridges.emplace_back(u, v);
}
}
}
}
public:
explicit TarjanBridges(int vertices)
: V(vertices), timer(0), adj(vertices), disc(vertices, 0), low(vertices, 0) {}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
std::vector<std::pair<int, int>> findBridges() {
bridges.clear();
std::fill(disc.begin(), disc.end(), 0);
std::fill(low.begin(), low.end(), 0);
timer = 0;
for (int i = 0; i < V; ++i) {
if (disc[i] == 0) {
dfs(i, -1);
}
}
return bridges;
}
};4. Topic 153: Advanced String Algorithms: Knuth-Morris-Pratt (KMP)
#1. The Non-Rewinding Search Invariant
#When matching pattern (length ) against text (length ):
- Naive matching rewinds the text index upon mismatch worst case.
- KMP Invariant: The text pointer moves strictly forward (). On mismatch, pattern pointer falls back using the precomputed (LPS) table.
2. The (LPS) Array
#stores the length of the longest proper prefix of that is also a suffix of :
| Pattern Char | a | b | a | b | a | c | a |
|---|---|---|---|---|---|---|---|
| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
| (LPS) | 0 | 0 | 1 | 2 | 3 | 0 | 1 |
TypeScript Implementation
export function buildLPS(pattern: string): number[] {
const m = pattern.length;
const lps = new Array(m).fill(0);
let len = 0;
let i = 1;
while (i < m) {
if (pattern[i] === pattern[len]) {
len++;
lps[i] = len;
i++;
} else {
if (len !== 0) {
len = lps[len - 1]; // Fallback to shorter prefix-suffix
} else {
lps[i] = 0;
i++;
}
}
}
return lps;
}
export function kmpSearch(text: string, pattern: string): number[] {
const n = text.length;
const m = pattern.length;
const matches: number[] = [];
if (m === 0) return matches;
const lps = buildLPS(pattern);
let i = 0; // Text pointer
let j = 0; // Pattern pointer
while (i < n) {
if (text[i] === pattern[j]) {
i++;
j++;
}
if (j === m) {
matches.push(i - j);
j = lps[j - 1];
} else if (i < n && text[i] !== pattern[j]) {
if (j !== 0) {
j = lps[j - 1]; // Skip redundant comparisons
} else {
i++;
}
}
}
return matches;
}Modern C++20 Implementation: KMP Pattern Matcher
#include <iostream>
#include <vector>
#include <string_view>
class KMPMatcher {
public:
static std::vector<int> computeLPS(std::string_view pattern) {
int m = static_cast<int>(pattern.size());
std::vector<int> lps(m, 0);
int len = 0;
int i = 1;
while (i < m) {
if (pattern[i] == pattern[len]) {
lps[i++] = ++len;
} else {
if (len != 0) {
len = lps[len - 1];
} else {
lps[i++] = 0;
}
}
}
return lps;
}
static std::vector<int> search(std::string_view text, std::string_view pattern) {
std::vector<int> occurrences;
int n = static_cast<int>(text.size());
int m = static_cast<int>(pattern.size());
if (m == 0) return occurrences;
std::vector<int> lps = computeLPS(pattern);
int i = 0; // Text index
int j = 0; // Pattern index
while (i < n) {
if (text[i] == pattern[j]) {
++i;
++j;
}
if (j == m) {
occurrences.push_back(i - j);
j = lps[j - 1];
} else if (i < n && text[i] != pattern[j]) {
if (j != 0) {
j = lps[j - 1];
} else {
++i;
}
}
}
return occurrences;
}
};5. Topic 154: Advanced Tree Techniques (Euler Tour & Binary Lifting)
#1. The Euler Tour Technique (Subtree Interval Mapping)
#By recording timestamps when entering and exiting vertices during DFS, a hierarchical tree is projected onto a 1D sequence:
- Record
in[u]upon visiting node . - Record
out[u]upon exiting node .
Key Invariant: The entire subtree rooted at node corresponds precisely to the contiguous 1D interval:
This projection converts complex subtree mutations and aggregations into standard 1D range queries executable on a Segment Tree or Fenwick Tree in time.
2. Binary Lifting for Lowest Common Ancestor (LCA)
#Binary lifting precomputes an ancestral jump table using dynamic programming:
Let up[u][k] denote the -th ancestor of vertex :
Modern C++20 Implementation: Binary Lifting LCA
#include <iostream>
#include <vector>
#include <bit>
#include <cmath>
class BinaryLiftingLCA {
private:
int n;
int maxLog;
std::vector<int> depth;
std::vector<std::vector<int>> up;
void dfs(int u, int p, int d, const std::vector<std::vector<int>>& adj) {
depth[u] = d;
up[u][0] = p;
for (int k = 1; k < maxLog; ++k) {
if (up[u][k - 1] != -1) {
up[u][k] = up[up[u][k - 1]][k - 1];
} else {
up[u][k] = -1;
}
}
for (int v : adj[u]) {
if (v != p) {
dfs(v, u, d + 1, adj);
}
}
}
public:
BinaryLiftingLCA(int numNodes, const std::vector<std::vector<int>>& adj, int root = 0)
: n(numNodes), depth(numNodes, 0) {
maxLog = std::max(1, static_cast<int>(std::log2(numNodes)) + 2);
up.assign(n, std::vector<int>(maxLog, -1));
dfs(root, -1, 0, adj);
}
int getLCA(int u, int v) const {
if (depth[u] < depth[v]) std::swap(u, v);
// 1. Equalize depths using binary powers
for (int k = maxLog - 1; k >= 0; --k) {
if (depth[u] - (1 << k) >= depth[v]) {
u = up[u][k];
}
}
if (u == v) return u;
// 2. Binary search jumps together right below the LCA
for (int k = maxLog - 1; k >= 0; --k) {
if (up[u][k] != up[v][k]) {
u = up[u][k];
v = up[v][k];
}
}
return up[u][0];
}
int getDistance(int u, int v) const {
int lca = getLCA(u, v);
return depth[u] + depth[v] - 2 * depth[lca];
}
};6. Topic 155: Advanced Dynamic Programming (Bitmask DP)
#Traveling Salesperson Problem (TSP)
#Given vertices and pairwise transition costs, find the minimum cost tour visiting every node once and returning to the origin.
- Brute Force Permutations: (Intractable for ).
- Held-Karp Bitmask DP: Encode visited subsets as an integer bitmask of length :
State Definition
dp[mask][u]: Minimum cost of traversing all vertices present in mask, currently located at vertex .
Recurrence Relation
- Total States:
- Transitions per State:
- Overall Runtime: , solving instances in approximately .
Modern C++20 Implementation: Held-Karp Bitmask DP
#include <iostream>
#include <vector>
#include <algorithm>
class TSPHeldKarp {
public:
static constexpr int INF = 1e9;
static int solve(int n, const std::vector<std::vector<int>>& dist) {
int totalStates = 1 << n;
// dp[mask][u] = min cost to visit subset `mask` ending at node `u`
std::vector<std::vector<int>> dp(totalStates, std::vector<int>(n, INF));
// Base case: starting at node 0 with mask (1 << 0)
dp[1][0] = 0;
for (int mask = 1; mask < totalStates; ++mask) {
for (int u = 0; u < n; ++u) {
if (!(mask & (1 << u)) || dp[mask][u] == INF) continue;
for (int v = 0; v < n; ++v) {
if (mask & (1 << v)) continue; // Already visited v
int nextMask = mask | (1 << v);
dp[nextMask][v] = std::min(dp[nextMask][v], dp[mask][u] + dist[u][v]);
}
}
}
// Return to start node 0 from all full tours
int minTour = INF;
int fullMask = (1 << n) - 1;
for (int u = 1; u < n; ++u) {
if (dp[fullMask][u] != INF) {
minTour = std::min(minTour, dp[fullMask][u] + dist[u][0]);
}
}
return minTour;
}
};7. Master Comparison: Advanced Paradigm Taxonomy
#| Subsystem | Core Paradigm | Canonical Algorithm | Asymptotic Complexity | Industrial Applications |
|---|---|---|---|---|
| Directed Graphs | Transposition + Double DFS | Kosaraju SCC | time, space | Compiler call-graph cycle detection, 2-SAT solvers |
| Undirected Graphs | DFS Discovery / Low-Link | Tarjan Bridges | time, space | Telecom critical failure links, network survivability |
| String Matching | Prefix-Suffix Finite Automaton | Knuth-Morris-Pratt | time, space | DNA sequencing, intrusion detection, packet inspection |
| Tree Subtree Queries | DFS Interval Projection | Euler Tour Flattening | build, query | Dynamic organizational hierarchies, DOM range updates |
| Ancestral Queries | Dyadic Powers Decomposition | Binary Lifting LCA | build, query | Phylogenetic trees, Git branch merges, router reachability |
| Permutation Optimization | Subset State Compression | Held-Karp Bitmask DP | time, space | Drone dispatch routing, VLSI circuit board drilling |
8. Authoritative Academic & Practice Compendium
#Primary Academic Literature
#- Kosaraju, S. R. (1978). Fast algorithms for connectivity and related problems. Technical Report, Johns Hopkins University.
- Tarjan, R. E. (1972). Depth-first search and linear graph algorithms. SIAM Journal on Computing, 1(2), 146–160.
- Knuth, D. E., Morris, J. H., & Pratt, V. R. (1977). Fast pattern matching in strings. SIAM Journal on Computing, 6(2), 323–350.
- Held, M., & Karp, R. M. (1962). A dynamic programming approach to sequencing problems. Journal of the Society for Industrial and Applied Mathematics, 10(1), 196–210.
- Bender, M. A., & Farach-Colton, M. (2000). The LCA problem revisited. Latin American Symposium on Theoretical Informatics (LATIN), 88–94.
Authoritative Visualizers & External Curricula
#- VisuAlgo Graph Traversal & Connectivity: Interactive visualizer for bridge detection and Kosaraju strongly connected components.
- VisuAlgo String Matching: Animated KMP LPS table construction and non-rewinding text pointer execution.
- GeeksforGeeks Advanced Data Structures & Algorithms: Comprehensive documentation on LCA binary lifting and Euler tours.
High-Yield LeetCode Practice Suite
#- LeetCode 28: Find the Index of the First Occurrence in a String (Medium) — Canonical Knuth-Morris-Pratt pattern matching.
- LeetCode 1192: Critical Connections in a Network (Hard) — Pure Tarjan low-link bridge identification.
- LeetCode 236: Lowest Common Ancestor of a Binary Tree (Medium) — Foundational LCA queries.
- LeetCode 847: Shortest Path Visiting All Nodes (Hard) — Bitmask BFS/DP state space compression.
- LeetCode 307: Range Sum Query - Mutable (Medium) — Complements Euler tour subtree queries with dynamic range data structures.