From Trie to Automaton
Insert every pattern into a trie. Each node is a state representing the string on its root path, each edge appends one symbol, and terminal nodes store pattern identifiers. Identifiers preserve duplicate patterns and caller metadata.
Trie edges handle successful extensions only. Aho–Corasick adds a failure link from each state to the deepest proper suffix of its represented string that is also a trie state. The root represents the empty string and fails to itself; its children fail to the root. On a missing edge, follow failures until a compatible edge appears or the root is reached.
After reading a text prefix, the current state represents its longest suffix that is also a prefix of some pattern. Sparse maps save memory for large alphabets; completed transition tables use more memory but make every state-symbol transition one lookup.
Failure and Output Links
Build failure links in breadth-first order so every parent and shallower transition is ready before its children. Root children fail to the root. For edge $v\xrightarrow{c}u$, begin at $\operatorname{fail}(v)$, follow failures while no $c$ edge exists, and set $\operatorname{fail}(u)$ to the discovered destination or root.
Failure chains enumerate progressively shorter dictionary suffixes. If a failure ancestor is terminal, its pattern ends at the same text position. Thus reaching she must report both she and its terminal suffix he. Implementations may merge inherited outputs during BFS or store an output link to the nearest terminal failure ancestor; reporting time is charged to emitted matches.
flowchart TD
ROOT["root"]
S["state s"]
SH["state sh"]
SHE["state she; output she"]
H["state h"]
HE["state he; output he"]
REPORT["report she and he"]
ROOT -->|s| S
S -->|h| SH
SH -->|e| SHE
ROOT -->|h| H
H -->|e| HE
SH -. failure .-> H
SHE -. failure/output .-> HE
HE -. failure .-> ROOT
SHE --> REPORT
Search and Reporting
For each text symbol, follow failure links until the current state has a matching edge or is the root, take the edge when present, and emit every pattern ID in the destination's output list. Search never resets after reporting, so overlapping matches and multiple patterns ending at one position are handled naturally.
With constant-time transitions, total pattern length $L$, text length $n$, and $z$ emitted matches, construction and search require $O(L+n+z)$ time under the standard automaton model. The $z$ term is unavoidable when the caller requests every match.
Counting and Automaton DP
To count all matches without listing them, store at each state the number of terminal patterns in its output/failure chain and add that value after each transition. To count occurrences per pattern, count visits to states during the scan, then propagate visit totals from deeper states to failure parents in reverse BFS order; a terminal node's propagated total is its pattern frequency.
The automaton is also a DP state graph. Mark states whose output chain contains a forbidden pattern, then let dp[length][state] count generated strings that remain in safe states. Transition on each alphabet symbol and discard forbidden destinations. This supports counting avoiding strings, lexicographically smallest valid strings, digit DP, shortest paths, and probability models. Large lengths can use matrix exponentiation over safe states.
For total pattern length $L$, text length $n$, and $z$ reported occurrences, Aho–Corasick runs in $O(L+n+z)$ time under constant-time automaton transitions; the $z$ term is required by the output itself. Counting modes replace explicit reporting with aggregated state counts and scan the text in $O(n)$ after construction.