Aho-Corasick algorithm Created 2026-10-20 Updated 2026-11-05
The Aho-Corasick algorithm reports occurrences of a finite collection of nonempty pattern strings in one string. It builds an Aho-Corasick automaton from their trie. Each scanned character extends the current state; when no edge matches, failure links find a shorter matching suffix. Report the current terminal pattern string and the terminal ancestors reached through output links.[ref]
Efficient String Matching: An Aid to Bibliographic Search by Alfred V. Aho and Margaret J. Corasick. abstract, keyword-matching machine and single-pass search
The state represents the longest suffix of the scanned text that is a prefix of some pattern string. This invariant explains correctness: a match ending now is exactly a terminal suffix represented by that state or a failure link ancestor. It also reports overlaps. With pattern strings
ab, bab, bc, scanning babc reports ab and bab at position three, then bc at position four.For a fixed-size alphabet, completed transitions and output links give total time, where is total pattern string length, text length and the number of reported occurrences. The term is unavoidable when listing every match. Store terminal output links instead of copying every inherited match list: copied suffix lists can greatly enlarge preprocessing.
Aho-Corasick automaton Created 2026-10-20 Updated 2026-11-05
An Aho-Corasick automaton augments a trie with a failure link from each nonroot state to its longest proper suffix that is also a trie prefix. Terminal states identify completed pattern strings; an output link points to the next terminal state along the failure link chain. The root represents the empty string. These structures support the Aho-Corasick algorithm.[ref]
For pattern strings
ab, bab, bc, the state bab has failure link to terminal ab, so both occurrences are reported after reading bab. If the next character is c, no edge leaves bab on c; following failure links reaches b, whose edge leads to bc. Reporting only the deepest terminal state would miss the simultaneous ab match.A completed transition table stores the final destination for every state and alphabet character, giving one table lookup per input character. A sparse representation stores only trie edges and follows failure links on demand. They implement the same state invariant; the relevant space and lookup costs depend on the alphabet representation.
Margaret J. Corasick 2026-11-05
Margaret J. Corasick coauthored the 1975 multi-keyword matching algorithm with Alfred V. Aho. Their Aho-Corasick algorithm builds a finite-state keyword-matching machine and scans the text once, reporting occurrences of all dictionary patterns.[ref]
A trie shares pattern prefixes, while failure links retain the longest useful suffix after a mismatch. For patterns
he and she, reaching the end of she must also report he; terminal suffix information is therefore part of matching, not merely following trie edges. The mechanism makes one text scan reusable across many search terms. Output link 2026-11-05
An output link in an Aho-Corasick automaton points to the nearest terminal state encountered strictly after the current state on its failure link chain. It skips states that do not end a pattern string. After reading a text character, report a pattern at the current state if terminal, then follow output links to report its terminal suffixes.
For dictionary strings
a, ba, aba, reaching state aba reports all three patterns ending at that position. Its output link reaches ba, and the next reaches a. Reporting only aba would miss two valid occurrences. The current state's own match is handled separately because an output link points to a proper suffix, not to itself.If is the failure link destination, the output link is when that state is terminal, and otherwise the output link of . Build this information in increasing trie depth, so the shorter failure states are already available. Traversing only terminal links makes reporting cost proportional to the number of emitted matches, rather than the length of every nonterminal failure chain.[ref]
Efficient String Matching: An Aid to Bibliographic Search by Alfred V. Aho and Margaret J. Corasick. Keyword-matching machine; terminal suffix reporting, with the three-pattern example independently derived
Traverse the trie along the input string, stopping as soon as the required child is absent. All words and letter collections in this question are assumed to use only lowercase letters The loop invariant is that
a through z; this is the alphabet represented by the given child array. Put the following methods inside the supplied Trie class.def contains(w: String): Boolean = {
var t: Trie = this
var i = 0
while (i < w.length && t != null) {
t = t.children(toIndex(w.charAt(i)))
i += 1
}
t != null && t.complete
}t is the trie node reached by the prefix w.take(i), or null if that prefix has no path, with . If t is null, no extension can be a stored word. Otherwise, following the next character preserves the loop invariant. The loop variant is . At exit,A prefix that exists but is not complete must return false. The empty string is present exactly when the root's complete flag is true. The time complexity is , with constant auxiliary space.Create only the missing nodes along the required trie path, and mark its last node complete.The loop invariant is that the path for
def add(w: String): Unit = {
var t: Trie = this
var i = 0
while (i < w.length) {
val k = toIndex(w.charAt(i))
if (t.children(k) == null) t.children(k) = new Trie
t = t.children(k)
i += 1
}
t.complete = true
}w.take(i) exists and ends at t, while the set of complete words remains unchanged from entry. A fresh node has all children null and complete == false, so creating an unfinished path introduces no stored word. The loop variant is . At termination the full path exists; setting its final flag givesExisting paths and terminal flags are untouched, so adding an existing word is idempotent. Adding the empty string marks the root. The time complexity is because the alphabet size is fixed; at most new trie nodes are allocated.Use trie search with a letter multiset. Counts avoid generating the same result repeatedly when the input contains equal letters. The backtracking explores a letter value once, irrespective of how many indistinguishable copies remain.At entry to
def allWords(ls: String): List[String] = {
val left = new Array[Int](26)
var i = 0
while (i < ls.length) {
left(toIndex(ls.charAt(i))) += 1
i += 1
}
var out = List.empty[String]
def visit(t: Trie, prefix: String): Unit = {
if (t.complete) out = prefix :: out
var k = 0
while (k < 26) {
val child = t.children(k)
if (left(k) > 0 && child != null) {
left(k) -= 1
visit(child, prefix + toChar(k))
left(k) += 1
}
k += 1
}
}
visit(this, "")
out.reverse
}visit, t is reached by prefix, and left(k) is the input multiplicity of letter minus its multiplicity in prefix. All these counts are nonnegative. Each recursive call consumes one available letter, and restoration makes sibling calls see the same counts as before that child. The recursion therefore terminates at depth at most , even when the dictionary contains longer words.A completed prefix is emitted once, and the traversal continues so that longer words with the same prefix are also found. Every emitted word is in the dictionary and uses at most the available multiplicities. Conversely, every dictionary word meeting those bounds has an existing trie path whose next letter has positive remaining count at every step; the traversal reaches its terminal node. A trie has a unique root path for each word, and each child is visited at most once per reached node. ThereforeThe boxed expression specifies the set of entries in the returned list, rather than its ordering. If the dictionary includes the empty string, the method includes it once. Counting costs ; visiting reachable nodes costs apart from string copying and output construction. The presented immutable-prefix construction additionally costs the sum of the prefix lengths it constructs. The recursion depth is at most , and the letter-count array has fixed size.
A trie node needs a terminal flag as well as its children: a stored word can also be the prefix of a longer stored word.Index zero denotes
TYPE Trie = POINTER TO Node;
Node = RECORD
terminal: BOOLEAN;
child: ARRAY 26 OF Trie
END;a, index 25 denotes z; null pointers denote absent branches. The set represented by a nonnull node consists of the empty string if terminal is true, together with every child's words prefixed by that child's letter. Represent the empty set by NIL. The singleton containing the empty string instead has a terminal root and no children.The declarations alone do not force a unique representation. A nonterminal leaf, or a whole branch without terminal descendants, contributes no word. To obtain uniqueness up to pointer addresses, impose a representation invariant: the structure is a finite rooted tree, and each allocated node lies on a path to a terminal node. Then precisely the prefixes of stored words exist, all flags are determined, and the empty set has only the
NIL representation. The insertion procedures below preserve this invariant when starting with such a trie. Trie Created 2026-10-20 Updated 2026-11-05
A trie stores strings in a rooted tree whose edges represent symbols; the path from the root spells a prefix. A separate terminal marker distinguishes a stored word from a prefix of another. Storing
to, tea and ten shares the initial t, and tea with ten also shares te. Lookup follows one edge per symbol, taking time for a length- key when child selection is constant-time. Dense child arrays use more memory than sparse maps. A trie naturally supports prefix queries and longest-prefix matching.[ref]https://raw.githubusercontent.com/kevin-wayne/algs4/master/src/main/java/edu/princeton/cs/algs4/TrieST.java. Class documentation: key operations, prefix queries and length-based costs
Trie search with a letter multiset Created 2026-10-31 Updated 2026-11-05
Use backtracking through a dictionary trie, decrementing and restoring the available count for each traversed letter. Visit each letter-labelled child once when its count is positive, rather than choosing between indistinguishable occurrences. Every reachable complete word is then produced once, including words using only a subcollection of the original letter multiset.
Trie terminal marker 2026-11-05
A trie terminal marker records that the path to a trie node is a complete stored key, independently of whether that node has children. Store
to and top: the node after to must be terminal even though its p child continues to top. Testing only for leaf nodes would incorrectly report that to is absent.Insertion sets the marker at the end of the key; membership requires both a matching path and a terminal marker. Deletion clears the marker and may prune nodes that have neither a marker nor children. The initial node can itself be terminal if the empty string is a stored key. A symbol outside the key alphabet can implement an explicit end marker instead, but it must be distinguished from ordinary key characters. These conventions separate exact-key lookup from prefix lookup and permit correct enumeration of keys that are prefixes of other keys.