Intuition The ONE core idea
Aho-Corasick answers a single question: "As I read a long text one letter at a time, which of my many search words just finished spelling themselves?" It answers this by pre-building one map — a tree of all the words plus emergency "shortcut ropes" — so every text letter costs one cheap step, no matter how many words you hunt for.
This page assumes nothing . Every arrow, every symbol, every word like "prefix" or "automaton" is built from the ground up below, in the order you need them.
Before you climb into the main note , you must own the vocabulary it speaks. We build it one brick at a time.
Everything here is about text : a row of symbols read left to right.
Definition Character, string, alphabet
A character is a single symbol, like h or s. We write one as c .
A string is a finite row of characters, like hers. We write one as s .
The alphabet is the whole set of characters we are allowed to use. We write it Σ (a Greek capital "S", read "sigma"). For lowercase English, Σ = { a , b , c , … , z } and its size ∣Σ∣ = 26 .
Why the symbol Σ ? Because later, cost formulas count "one slot per possible next letter" , and that number of slots is ∣Σ∣ . Naming the alphabet lets us talk about that cost.
Look at the figure: a string is just cells in a line, each cell holding one character. The little index numbers under the cells will matter the moment we talk about positions — "the match ended at position 4".
These two words are the heart of the whole algorithm. Get them wrong and nothing else lands.
Definition Prefix and suffix
Take a string s .
A prefix is any chunk you get by chopping off the end — you keep the front. Prefixes of hers: "" (empty), h, he, her, hers.
A suffix is any chunk you get by chopping off the front — you keep the back. Suffixes of hers: hers, ers, rs, s, "".
A proper prefix/suffix is one that is not the whole string — you must have chopped off at least one character.
Intuition Picture them as a sliding window
Imagine hers written on paper. Slide a bracket in from the right edge and everything to its left is a prefix . Slide a bracket in from the left edge and everything to its right is a suffix . The empty string "" sits at both extremes — it is both a prefix and a suffix of everything.
Why does Aho-Corasick obsess over these? Because the machine's whole job is:
"The longest prefix of some search word that is also a suffix of the text I've read so far ."
That single sentence uses both concepts at once. When a letter fails to extend a match, the "shortcut rope" jumps you to the longest proper suffix of where you were that is still a prefix of some word. Both halves, again. Hold onto this — it returns in Step 2 of the parent note.
We do not search for one word; we search for a dictionary of words at once.
Definition The pattern set and its total length
A set is an unordered collection of distinct things — order does not matter and duplicates are not allowed (unlike a shopping bag, which may hold two apples, a set holds each item at most once). We write it in curly braces: { he , she , his , hers } .
We call each search word a pattern and name them p 1 , p 2 , … . The subscript i is just a counter: p 1 = he, p 2 = she, and so on.
∣ p i ∣ means the length (number of characters) of pattern i . So ∣ he ∣ = 2 .
M = ∑ i ∣ p i ∣ is the total length of all patterns added up . The symbol ∑ (capital sigma again, here meaning "sum") says "add the thing on the right for every i " .
Worked example Adding up the dictionary
For { he , she , his , hers } :
M = ∣ he ∣ + ∣ she ∣ + ∣ his ∣ + ∣ hers ∣ = 2 + 3 + 3 + 4 = 12.
Why do we need M ? Because the build cost of the machine is measured in M (and ∣Σ∣ ), not in "number of patterns". Two patterns of length 1000 cost the same to build as 2000 patterns of length 1 — what matters is total characters. Naming M lets the complexity line say that precisely.
The machine is a graph . If that word scares you, it is just dots joined by arrows.
Definition Node, edge, directed graph, tree, root
A node (also "vertex") is a dot. We name one v or u .
An edge is an arrow from one node to another. A labeled edge also carries a character, e.g. "u c v " means "an edge from node u to node v tagged with character c ". (We use u and v for nodes; the letter p stays reserved for patterns from §2.)
A directed graph is a set of nodes and arrows where arrows point one way.
A tree is a special graph with no loops, where every node has exactly one parent-arrow pointing into it — except one special node.
The root is that one special node with no parent. It is where everything starts. We call it root.
The parent of v is the node the arrow into v came from; v is its child .
The depth of a node is how many edges you cross from the root to reach it. Root has depth 0 .
Why a tree and not a plain list? Because words that start the same (he and hers both start he) can share their front edges, so the machine stores he once. That sharing is exactly what a tree gives you and a list does not.
Intuition Depth = string length
In our machine, following one edge means "read one more character", so a node at depth d stands for a string of length d . This is why we can process nodes "by increasing depth" and know we are going "shortest string first" — depth and string-length are the same ruler.
Now combine "tree" with "prefix" and you get the first real structure.
Definition Trie (prefix tree)
A trie is a tree where:
Every edge is labeled by a character.
The root stands for the empty string "".
Every node stands for exactly one string: read the edge-labels along the path from the root to that node.
Inserting a word hers means walking h→e→r→s from the root, creating any missing child, and marking the last node as terminal (a word ends here).
Intuition Why "prefix tree"?
Every node is a prefix of some pattern, because the path from the root spells that prefix. Two words sharing a front share that path. he and hers walk the same h→e edges, then hers continues r→s. The trie merges shared prefixes for free — that is the whole point.
We go deeper on the trie itself in Trie — prefix tree ; here you only need: node = a prefix, edge = a character, terminal = a word ends .
We name three operations on it, matching the parent note:
str ( v ) = the string that node v stands for.
go [ v ] [ c ] or child ( v , c ) = the node you reach from v by following the real edge labeled c (may not exist).
terminal : a node flagged because a full pattern ends there.
This is the one genuinely new idea; the parent note derives it fully. Here we only make sure the symbol and picture are clear.
fail [ v ]
fail [ v ] is the node standing for the longest proper suffix of str ( v ) that is also a node of the trie (i.e. still a prefix of some pattern). If no such non-root node exists, fail [ v ] = root .
Edge case — the root itself. The root stands for the empty string, which has no proper suffix to fall back to, so we define fail [ root ] = root (it points to itself and is never actually followed). The direct children of the root also get fail = root , since their only proper suffix is the empty string.
Intuition The rope, pictured
You are at node she (you have matched she in the text). The next text letter does not fit. You do not want to throw away all progress and crawl back to the root. The failure link is a rope that drops you to the node for he — because he is the longest tail of she that is still the beginning of a real word. You keep as much matched material as possible.
Notice: fail always points to a shorter string (a proper suffix is strictly shorter). Shorter string = smaller depth = higher up the tree. A failure rope never drops you deeper.
That "always shorter / always higher" fact is why we build failure links in a specific order — see the next brick.
This idea is borrowed directly from single-pattern matching. If you have seen KMP — single pattern matching , the failure link is exactly KMP's prefix function, but spread over a whole tree of words instead of one word.
To fill in every failure link correctly, we must visit nodes in order of depth . The tool that does that is BFS.
Definition BFS (breadth-first search)
BFS explores a tree/graph level by level : first the root, then everything at depth 1, then everything at depth 2, and so on. It uses a queue (a first-in-first-out line): you push the root, then repeatedly pop the front node and push its children.
Intuition Why BFS and not the "obvious" recursion?
fail [ v ] depends on failure links of shorter strings (from Step 5, the rope only goes to shorter strings). BFS guarantees every shorter string was already finished before we touch v . If instead you recursed down the tree, you would try to compute a node's rope before the shorter node it points to was ready — and get garbage. Order = by depth = BFS. Full treatment lives in BFS — breadth-first search .
Finally, the word in the topic title: automaton .
Definition Finite automaton and the transition
δ
A finite automaton is a machine with a finite set of states (here: the trie nodes) and a rule that, given a current state and an input character, tells you the next state. You feed it characters and it moves.
Its transition function is written δ (Greek lowercase "delta"). δ ( v , c ) = "if I'm in state v and read character c , which state do I go to?"
The complete transition never fails: if there is no real trie edge on c , δ follows the failure rope(s) for you and always lands somewhere (root at worst). So every step is one lookup , O ( 1 ) .
Intuition Why turn the trie into an automaton?
Without δ , on a mismatch you might climb several failure ropes for one text letter. With δ pre-filled, each text letter is exactly one table lookup — that is what makes scanning O ( n ) , independent of how many patterns you loaded. This connects to general Finite Automata — DFA/NFA theory: Aho-Corasick's finished machine is a deterministic finite automaton.
There is a whole family of string machines built on suffixes rather than a fixed dictionary — see Suffix Automaton and Suffix Tree — and other matching engines like the Z-algorithm and string matching . Aho-Corasick is the "many fixed patterns at once" specialist.
Last brick: the letters in the complexity line, so it reads as English.
Definition The cost alphabet
n = length of the text we scan.
M = total length of all patterns (Step 2).
∣Σ∣ = alphabet size (Step 0).
z = number of matches actually reported .
O ( ⋅ ) (big-O ) means "grows no faster than", ignoring constant factors — a way to compare how work scales as inputs get big. O ( n ) means "work grows in step with text length".
pattern set and total length M
automaton and transition delta
Read it upward: characters make strings; strings give prefixes/suffixes and a dictionary; graphs give trees, trees give the trie; prefix + suffix + BFS give failure links; trie + failure links give the automaton; add the cost symbols and you have the full Aho-Corasick machine.
Cover the right side and answer aloud; if any stalls, reread that section before the main note.
What is a proper suffix of hers, and list them all A suffix that is not the whole string: ers, rs, s, "".
What single string is both a prefix and a suffix of every string The empty string "".
In the trie, what does a node at depth d represent A string (prefix of some pattern) of length exactly d .
What does M = ∑ i ∣ p i ∣ measure The total number of characters across all patterns added together.
Where does fail [ v ] point — shorter or longer string, and which one exactly To a strictly shorter string: the longest proper suffix of str ( v ) that is still a trie node (or root).
What is fail [ root ] , and why It is defined as the root itself (and never followed), because the empty string has no proper suffix to fall back to.
Why compute failure links with BFS and not recursion fail [ v ] needs failure links of shorter strings; BFS finishes all smaller depths first.
What does δ ( v , c ) give you when v has no real edge on c The state you reach by following the failure rope(s) — it never fails, worst case root.
In the cost O ( n + z ) , what is n The length of the text being scanned.
In the cost O ( n + z ) , what is z The number of matches actually reported during the scan.
Why is scanning independent of the number of patterns With δ precomputed, each text char is one O ( 1 ) lookup regardless of dictionary size.