Proofs: McCreight's algorithm
These are the human proofs of McCreight's algorithm, at full length.
McCreight's algorithm builds the suffix tree in linear time full
This proof shows: correctness, termination, complexity.
Let with , where occurs only at the end, and . For , is the longest prefix of that is a prefix of some , ; is the empty string. Since occurs once, no is a prefix of an earlier (its would have to be at a position where has a symbol of ), so . Let be the compact trie of . A place is a node or a point inside an edge; each prefix of some , , has exactly one place in .
Lemma 1. In , the place of is a node (the root, or an inner node with at least two children), and ends at a leaf whose parent is this node.
Proof. In , follows the path of and then leaves it, since the next symbol of does not continue in any earlier suffix. Adding adds one leaf edge at the place of , which becomes a node with at least two children (or is the root).
Lemma 2 (McCreight). If with a symbol , then is a prefix of .
Proof. is a prefix of and of some , . So is a prefix of and of with .
Lemma 3. If is an inner node of , then has a place in ; if is an inner node of (it existed before step ), then is a node of , hence of .
Proof. The first claim is Lemma 2's proof. For the second, an inner node of has two children starting with different symbols , so and are prefixes of two suffixes with , and , are prefixes of and with . So branches in and is a node there. Since nodes are never removed and edges are only split, it stays a node in later trees.
Invariant. Before step (): the tree is ; is the node of ; every inner node has equal to the node of its string without the first symbol; and has a link if and only if it is the root or it is an inner node of .
It holds before step 1: the tree is the root with the leaf of , and is the root, whose link is the root.
Step . Let when is not the root.
Case L3, is the root. is empty, and the scan starts at the root, which is the place of the empty prefix of .
Case L4, has a link. By the invariant and Lemma 3, is the node , and is a prefix of by Lemma 2.
Case L5 to L7, has no link. Then was created in step by a split. Let be its parent and the edge label, so . If is the root, is without its first symbol and the rescan starts at the root. Otherwise has a link to the node of without its first symbol, and is that string followed by . In both cases is in the tree (Lemma 3), so the rescan, which at each node takes the child whose edge starts with the next symbol of and skips the whole edge if it is not longer than the rest of , reaches the place of without comparing other symbols: every edge on the way is on the path of , and its symbols agree with because is in the tree. If the place is inside an edge, the split creates a node there.
Now set , the node of . This is the correct link. If was just created by a split, then the place of had exactly one continuing symbol in . Since was created in step by a split at the place where left the tree, is a prefix of an earlier suffix and a prefix of for some . Then occurs in , so , and is a prefix of with . Hence leaves the tree right after , and .
Line L8. Otherwise is a node of , a prefix of . The scan compares symbols of after along the tree until the first mismatch, which exists since ; the place reached is the longest prefix of in , which is . A split there makes it a node.
Lines L9, L10. Adding the leaf of at the node of gives by Lemma 1. The only inner node created in step other than by the rescan split is the node of ; the rescan split node is itself in that case. All older inner nodes, and , now have correct links. The new has a link exactly when it is the root or an old node. So the invariant holds before step .
After step the tree is , the compact trie of all suffixes of , which is the suffix tree. The loop has steps, and each walk goes down a finite tree, so the algorithm terminates.
Time. Write for the string depth and for the node depth (the number of nodes on the root path, including the root) of a place .
Scans. The scan of step starts at the node of , with , and makes at most symbol comparisons. The sum over is at most .
Rescans. For a node with a link, . Indeed, each ancestor of other than the root is an inner node with a link, and is the node of without its first symbol, which is a prefix of the string of ; so is an ancestor of or itself. Distinct ancestors have distinct string depths, so their links are distinct. This gives distinct nodes on the root path of , and . The rescan of step starts at with and ends at , each step going down one node. The scan only goes further down, so the number of rescan steps is at most . The sum is at most .
Each scan comparison and each rescan step costs one child lookup and more work; the remaining work in a step is . So the total time is child lookups, which is for a constant alphabet or with hashing, and with balanced child maps.