Definition · The string-matching automatonFor a pattern p of length m over an alphabet A, the matching automaton has states 0, 1, ..., m and a transition function delta defined by: delta(j, c) is the length of the longest prefix of p that is a suffix of the string p[0..j−1] followed by c. State m is accepting. The machine starts in state 0 and reads the text one character at a time.
The definition is a statement about strings and mentions no algorithm, which is why the table can be built by the obvious method — for each state and each character, form the string and try every prefix length downwards — and why the lab does exactly that, writing (m + 1) times sigma cells and counting each write.
Theorem · The state after reading a prefix of the text is the longest match ending thereIf the machine is started in state 0 and fed t[0..i], it ends in the state equal to the length of the longest prefix of p that is a suffix of t[0..i]. Consequently it is in state m exactly when an occurrence of p ends at position i.
By induction on i. Before anything is read the longest prefix of p that is a suffix of the empty string is the empty prefix, of length 0, and the machine is in state 0.
Suppose after t[0..i−1] the machine is in state j, the length of the longest prefix of p that is a suffix of t[0..i−1]. Any prefix of p that is a suffix of t[0..i] and has length at least 1 has its last character equal to t[i] and its first part a suffix of t[0..i−1] that is also a prefix of p, hence of length at most j.
So the candidates are exactly the prefixes of p that are suffixes of p[0..j−1] followed by t[i], and the longest of those is delta(j, t[i]) by definition. The machine moves there, which completes the induction. State m then means the whole pattern is a suffix of what has been read, which is an occurrence ending at i.
□
Example · Eighteen cells, and the transition that carries the borderThe lab opens on the pattern ababc over the alphabet {a, b, c}, so there are 6 states and 18 cells. The row for state 4 — abab matched — reads a → 3, b → 0, c → 5. The first of those is the interesting one: reading an a after abab gives ababa, whose longest suffix that is a prefix of the pattern is aba, so the machine drops to 3 rather than restarting at 0 or advancing to 5. Everything the failure function would have computed at run time is sitting in that cell.
The run over abababcababcabababc takes exactly 19 steps for 19 characters and finds 3 occurrences, at offsets 2, 7 and 14. The counter for character comparisons does not exist on this page, because the algorithm makes none.
The trade, in two numbers that move in opposite directions
KMP makes 21 comparisons on that same text and stores 5 numbers. The automaton makes 0 comparisons, takes 19 steps, and stores 18. Neither is dominant: the automaton is ahead on the run and behind on the setup, and which matters depends on how many texts one table will be used for.
preset pattern m sigma states cells n run KMP cells/char
ababc ababc 5 3 6 18 19 19 21 18/19
aaab aaab 4 2 5 10 15 15 18 2/3
binary 10110 5 2 6 12 36 36 43 1/3
distinct abcd 4 4 5 20 14 14 16 10/7
wide the 3 13 4 52 38 38 40 26/19
Read the last two columns together. The run column is the text length in every row, with no exceptions and no dependence on the characters. The cells column moves by a factor of five across the same rows, and it moves with sigma rather than with m: the is the shortest pattern in the table and has the largest table, because its text uses thirteen distinct characters.
The last column is cells per text character, and it is the number that decides. Below 1 the table is cheaper than the text it will read; above 1 it is not, and a single search would have been better served by KMP. The binary preset sits at 1/3 and the wide one at 26/19, on the same algorithm.
Why KMP is what actually gets used
The automaton's table is (m + 1) times sigma. For byte data that is 256 cells per state, and for Unicode text it is not a table one writes out at all. KMP stores m integers regardless of the alphabet and pays for it with a fallback loop whose total cost is bounded by the same potential argument that bounds the construction.
So the automaton is the clearer explanation and the worse implementation, and it is worth building once for exactly that reason: delta(j, c) is what fail[] is compressing, and a reader who has seen the table knows what the fallback chain is walking through.