Advanced Graphs, Strings & Dynamic Programming
Tarjan's and Kosaraju's Strongly Connected Components (SCC), Bridges & Articulation Points, KMP string prefix function, and Bitmask DP.
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. 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
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:
Proof: If , there exists a cycle or back-edge from or its descendants reaching or an ancestor of . If , no alternate route exists, and severing isolates 's subtree.
3. 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 |
For prefix "ababa" at index 4, the longest proper prefix matching a suffix is "aba" of length 3.
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;
}4. 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 :
export class BinaryLiftingLCA {
private up: number[][];
private depth: number[];
private maxK: number;
constructor(n: number, adj: number[][], root: number = 0) {
this.maxK = Math.floor(Math.log2(Math.max(1, n))) + 1;
this.up = Array.from({ length: n }, () => new Array(this.maxK).fill(-1));
this.depth = new Array(n).fill(0);
this.dfs(root, -1, 0, adj);
}
private dfs(u: number, parent: number, d: number, adj: number[][]): void {
this.depth[u] = d;
this.up[u][0] = parent;
for (let k = 1; k < this.maxK; k++) {
if (this.up[u][k - 1] !== -1) {
this.up[u][k] = this.up[this.up[u][k - 1]][k - 1];
}
}
for (const v of adj[u]) {
if (v !== parent) {
this.dfs(v, u, d + 1, adj);
}
}
}
public getLCA(u: number, v: number): number {
// 1. Ensure u is at least as deep as v
if (this.depth[u] < this.depth[v]) {
[u, v] = [v, u];
}
// 2. Lift u to the same depth as v
for (let k = this.maxK - 1; k >= 0; k--) {
if (this.depth[u] - (1 << k) >= this.depth[v]) {
u = this.up[u][k];
}
}
if (u === v) return u;
// 3. Lift both nodes together below the LCA
for (let k = this.maxK - 1; k >= 0; k--) {
if (this.up[u][k] !== -1 && this.up[u][k] !== this.up[v][k]) {
u = this.up[u][k];
v = this.up[v][k];
}
}
return this.up[u][0];
}
}5. 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 .
6. Algorithmic Domain Summary
#| Subsystem | Core Paradigm | Canonical Algorithm | Asymptotic Complexity |
|---|---|---|---|
| Directed Graphs | Transposition + Double DFS | Kosaraju SCC | time, space |
| Undirected Graphs | DFS Discovery / Low-Link | Tarjan Bridges | time, space |
| String Matching | Prefix-Suffix Finite Automaton | Knuth-Morris-Pratt | time, space |
| Tree Subtree Queries | DFS Interval Projection | Euler Tour Flattening | build, query |
| Ancestral Queries | Dyadic Powers Decomposition | Binary Lifting LCA | build, query |
| Permutation Optimization | Subset State Compression | Held-Karp Bitmask DP | time, space |
References & Academic Attribution
#- Kosaraju, S. R. (1978). Fast algorithms for connectivity and related problems. Unpublished technical report.
- 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.