Properties and Observations - Preparation to IOI Part 6
Aho Corasik
Properties and Observations
Properties and Observations
Назария
Дарси 2 аз 2
Хониш: 11 дақиқа
A suffix-link chain can be long
Keep the notation from the first lesson: link(v) is the suffix link of state v, and go(v,c) consumes character c. Patterns are nonempty. Let M be their total length, counting all dictionary entries.
A suffix link shortens the represented string, but it may remove only one character. With the single pattern for , where the exponent means repeated copies of , every nonempty prefix links to . Starting at the final state takes links to reach the root. Only the final state is terminal.
Now make every nonempty prefix a pattern. The trie and its suffix links stay exactly the same, but every nonroot state becomes terminal. Compare the two dictionaries below.
Интерактивсанҷида бин!
This separates two quantities: how many suffix links exist on the path, and how many distinct patterns occur along it. The second quantity has a stronger bound in terms of the dictionary's total length.
Why the number of terminal suffixes is a square root
Fix a state and let h be the number of terminal vertices among that state and its suffix-link ancestors, excluding the root. Each terminal represents one distinct matching string. Their lengths are positive and different, since suffix links strictly decrease length.
List those lengths increasingly as ℓ1<ℓ2<⋯<ℓh. The jth length is at least j. These are lengths of actual dictionary patterns, so their sum cannot exceed M. Consequently,
2h(h+1)h≤M,≤⌊28M+1−1⌋.
Thus a state has O(M) distinct terminal suffixes. The bound is tight: choose a,aa,…,ar. At state ar, all r patterns match, while M=r(r+1)/2. The trie has only r+1 vertices, so the bound depends on total pattern length, rather than the number of trie vertices.
If all dictionary strings are distinct, a text of length n therefore has at most O(nM) occurrences in total. Duplicate IDs need separate treatment: r copies of the one-character pattern a give r reports at one position, although only one terminal vertex is involved.
The reporting loop from the first lesson also visits nonterminal vertices. Its whole suffix chain can still be long; the square-root bound counts matching terminals, not every step of that loop.
Check your reasoning
Take b,ab,aab,…,ar−1b. No pattern is a prefix of a longer one: where the shorter pattern has its final b, the longer one still has a. All r patterns end at the final position of ar−1b, and their total length is r(r+1)/2.
Интерактивсанҷида бин!
If no pattern is a proper suffix of another, at most one distinct pattern can end at any position. Two different strings ending there would have different lengths, and the shorter would be a proper suffix of the longer.
Long chains still allow linear transition work
Assume the suffix links are known and resolve transitions directly: when the required trie edge is missing, follow suffix links until an edge is available or the root is reached. Here we count the links followed while finding the next search state, without the separate reporting traversal.
Use the depth of the current trie state, meaning its represented string's length, as a running balance. Taking a trie edge consumes one character and increases depth by exactly 1. Following a suffix link decreases depth by at least 1. A missing character at the root consumes a character without changing depth.
There are at most n increases while reading n characters. Starting from depth 0, we cannot spend more depth than we have gained. Therefore at most n nonroot suffix-link steps occur across the whole scan, even if one character triggers a long chain.
This is an amortized bound: an expensive transition is paid for by earlier increases. Reporting has a different behavior. Its temporary vertex walks toward the root while the search state stays in place, so those decreases do not lower the balance used by the next transition.
Check your reasoning
The maximum is n−1. Let E be the number of consumed trie edges and F the number of suffix-link steps. If the final state is nonroot, at least one unit of gained depth remains, so F≤E−1≤n−1.
If the final state is the root, the last character could not have been consumed by a trie edge, which always ends at positive depth. Thus E≤n−1, and F≤E≤n−1 again.
Use the dictionary {a} and text an. The first character takes the root's a edge. Every later character first follows the suffix link back to the root, then takes the same edge. Exactly n−1 suffix-link steps occur.
Suffix links form a second tree
For each nonroot vertex v, make link(v) its parent. Omit the root's self-link. Every nonroot vertex has one parent, and repeatedly taking parents strictly decreases string length until reaching the root. These edges therefore form a rooted suffix-link tree.
Reuse D=[ab,bab,b,bc] and the state numbers from the first lesson. The drawing below shows this second tree. Each edge connects a suffix-link parent to its child.
Интерактивсанҷида бин!
A vertex u is an ancestor of v in this tree, including u=v, exactly when str(u) is a suffix of str(v). Going upward follows all represented suffixes. Equivalently, the subtree of u contains precisely the states whose strings end with str(u).
This turns a matching condition into a tree condition: a pattern ending at u occurs at the current text position exactly when the current state lies in u's subtree.
Count matches through the tree
Count all occurrences during the scan
Let term(v) be the number of pattern IDs stored at v. We want match(v) to count all IDs ending at the current position when the state is v. The current vertex contributes its own IDs; its remaining matching suffixes are those of its suffix-link parent. Set match(0)=0. For a nonroot vertex,
match(v)=term(v)+match(link(v)).
Compute these values once for the tree, then add the current state's value after each text character. This counts overlapping occurrences and duplicate pattern IDs. With transitions available, all values and the text total take O(S+n) time, where S is the number of states. A large number of occurrences does not force us to list their positions.
Count occurrences of each pattern
For separate pattern frequencies, start with visit(v): the number of text positions whose current state is exactly v. The subtree property tells us that the occurrence count at a terminal u is the sum of these visits over its subtree:
occ(u)=v∈subtree(u)∑visit(v).
Initialize each counter with its visit count. After collecting everything below a vertex, add its counter to its suffix-link parent. A traversal that processes children before their parent computes all subtree sums. Processing states in decreasing trie depth is also valid, because every suffix-link parent represents a shorter string.
Follow the accumulation for T=babc. Every vertex stays visible while a counter is added upward.
Интерактивсанҷида бин!
Each visit contributes once to every suffix-link ancestor, exactly the patterns that end at that position. After accumulation, a terminal's counter is therefore its number of occurrences. Duplicate IDs stored at the same terminal receive the same frequency.
The scan and one tree traversal take O(n+S) time after the automaton's transitions are ready. Reading the result for all k pattern IDs adds O(k) work. The full computation, including the first lesson's automaton construction for 26 letters, takes O(M+26S+n+k) time and requires O(S) extra counting storage.
You will learn
Distinguish the length of a suffix-link chain from the number of matching patterns on it.
Prove the square-root bound on distinct terminal suffixes and show when it is attained.
Explain why transition fallbacks take linear time over an entire text.
Use the suffix-link tree to count matches without listing their positions.
Activity
Suppose no dictionary pattern is a proper prefix of another. For every integer r≥1, can a single text position still end r distinct patterns with total length Θ(r2)? Construct such a family over the alphabet and justify it. What changes if no pattern is a proper suffix of another?
Extra challenge
For a text of length n≥1, find the exact maximum number of nonroot suffix-link steps used to resolve transitions, over all nonempty dictionaries and texts. Start at the root, exclude reporting steps, and do not count a root self-loop as a suffix-link step. Give a matching construction.