Algorithm Search⌘K

Proofs: Blumer et al. suffix automaton algorithm

These are the human proofs of Blumer et al. suffix automaton algorithm, at full length.

EXTEND turns the suffix automaton of t into that of tc, in amortized constant steps full

This proof shows: correctness, complexity.

We use the pseudocode and the notation of the page. For a text tt of length ii and a substring ww, endpost(w)endpos_t(w) is the set of end positions of the occurrences of ww, with endpost(ε)={0,…,i}endpos_t(\varepsilon) = \{0, \ldots, i\}. A class is a set of substrings with the same end set, and [w]t[w]_t is the class of ww.

Facts on classes. (F1) If uu is a suffix of ww, then endpos(w)⊆endpos(u)endpos(w) \subseteq endpos(u). If two substrings xx and yy end at a common position, one is a suffix of the other. Hence the strings of a class are the suffixes of its longest string with lengths in an interval, and if yy is a substring with endpos(y)⊆endpos(x)endpos(y) \subseteq endpos(x), ∣y∣≥∣x∣\lvert y \rvert \ge \lvert x \rvert, then xx is a suffix of yy. (F2) endpos(wa)={e+1:e∈endpos(w),te+1=a}endpos(wa) = \{e + 1 : e \in endpos(w), t_{e+1} = a\}, so [wa][wa] depends only on [w][w] and aa. (F3) If xx is the longest suffix of the longest string yy of a class CC that is not in CC, then xx is the longest string of its class: a longer x′∈[x]x' \in [x] would end at each end of yy (since endpos(y)⊆endpos(x)=endpos(x′)endpos(y) \subseteq endpos(x) = endpos(x')), so by (F1) x′x' would be a suffix of yy longer than xx outside CC, or yy would be a suffix of x′x' and then all three end sets are equal and x∈Cx \in C.

The invariant (A). For the current text tt: the states other than 0 are the classes of nonempty substrings, and 0 is the class of ε\varepsilon; len(v)len(v) is the length of the longest string of vv; link(v)link(v) is the class of the longest suffix of that string outside vv; δ(v,a)=[wa]\delta(v, a) = [wa] for w∈vw \in v when wawa is a substring, and undefined otherwise; last=[t]last = [t]. By (F1) the link path from lastlast visits, in order of decreasing length, classes that together contain each suffix of tt once. (A) holds for the empty text.

What changes when cc is appended. Let t′=tct' = tc and let ss be the longest suffix of t′t' that is a substring of tt (s=εs = \varepsilon if there is none). For ww a substring of t′t', endpost′(w)endpos_{t'}(w) is endpost(w)endpos_t(w), plus i+1i + 1 if ww is a suffix of t′t'.

  1. The suffixes of t′t' longer than ss are new and end only at i+1i + 1: they form one class CnewC_{new}, with longest string t′t' and suffix link [s]t′[s]_{t'}, by (F3) applied in t′t'.
  2. The classes of tt whose strings are all suffixes of t′t', or all not, keep the same strings.
  3. At most one class of tt splits, the class Q=[s]tQ = [s]_t when ss is not its longest string. Indeed, let CC be a class of tt with a suffix xx of t′t' and a string yy that is not, so ∣x∣<∣y∣\lvert x \rvert < \lvert y \rvert by (F1) and ∣x∣≤∣s∣\lvert x \rvert \le \lvert s \rvert, so xx is a suffix of ss. Then yy ends at each end of ss; if ∣y∣≤∣s∣\lvert y \rvert \le \lvert s \rvert, yy would be a suffix of ss and of t′t', so ss is a suffix of yy and endpos(y)⊆endpos(s)⊆endpos(x)=endpos(y)endpos(y) \subseteq endpos(s) \subseteq endpos(x) = endpos(y), so s∈Cs \in C. Then QQ splits into QshortQ_{short}, its strings of length at most ∣s∣\lvert s \rvert (all suffixes of ss, which gain i+1i + 1), and QlongQ_{long}, the longer ones.

The walk of line L2. For a state pp on the link path of lastlast, all its strings uu have ucuc a substring of tt or none do, by (F2); and if ucuc is a substring so is u′cu'c for each suffix u′u' of uu. So the walk passes exactly the states of the suffixes uu of tt for which ucuc is new, that is uc∈Cnewuc \in C_{new}, and setting δ(p,c)=cur\delta(p, c) = cur is the transition of (A) in t′t'. It stops at the state pp whose longest string ww is the longest suffix of tt with wcwc a substring, so s=wcs = wc and ∣s∣=len(p)+1\lvert s \rvert = len(p) + 1; or it runs off the path, and then s=εs = \varepsilon and line L3 sets link(cur)=0link(cur) = 0. Line L1 sets len(cur)=len(last)+1=i+1len(cur) = len(last) + 1 = i + 1, the length of t′t'.

Line L4. Here q=δ(p,c)=[s]tq = \delta(p, c) = [s]_t. If len(q)=∣s∣len(q) = \lvert s \rvert, then ss is the longest string of qq, all strings of qq are suffixes of ss, and no class splits. The link of curcur is [s]=q[s] = q, as item 1 requires. No other state changes its strings, so their lengths, links and transitions stay right; a transition into CnewC_{new} comes from a state of a string uu with ucuc a suffix of t′t' longer than ss, which is a state passed by the walk; and curcur has no transitions, since its strings end only at the last position.

Lines L5 to L7. If len(q)>∣s∣len(q) > \lvert s \rvert, qq splits. The clone is QshortQ_{short}: its longest string is ss, so its length is len(p)+1len(p) + 1; its link is the class of the longest suffix of ss below QQ, which is the old link(q)link(q); and qq becomes QlongQ_{long}, with the same length and the link QshortQ_{short}. Transitions out of the two parts: a transition from qq never led to qq itself, since an end set is not its own shift by one (compare the largest elements), so the targets are classes other than QQ, which keep their identity, and the targets of QshortQ_{short} and QlongQ_{long} are those of QQ, by (F2) in t′t' applied to strings that had the same targets in tt. So copying the transitions of qq to the clone is right. Transitions into QQ: a state xx with δ(x,a)=q\delta(x, a) = q must now go to QshortQ_{short} if its strings uu have uaua in QshortQ_{short}, which means uaua is a suffix of t′t', so a=ca = c and uu is a suffix of tt; so xx is on the link path of the old lastlast. Along that path, the targets on cc are the classes of ucuc for suffixes uu of decreasing length, and the ucuc in QQ have lengths in an interval, so these states are pp and its consecutive link ancestors up to the first one whose cc-transition goes elsewhere. Line L6 redirects exactly these. The link of curcur is [s]t′=Qshort[s]_{t'} = Q_{short}, set at line L7. A state vv whose old link was qq had as link the longest string of qq (by (F3)), which lies in QlongQ_{long}, so its link stays qq. The rest is as in the case of line L4.

So (A) holds for t′t', and line L8 sets last=[t′]last = [t']. By induction, after the whole text the automaton is the automaton of the classes. With the states on the link path from lastlast as accepting states, the automaton accepts the suffixes of tt, and it is the minimal automaton of this language: two strings uu and vv are right-equivalent for it if and only if they are both non-substrings or have the same end set, since the strings zz with uzuz a suffix of tt are exactly the te+1⋯tnt_{e+1} \cdots t_n for e∈endpos(u)e \in endpos(u), and these determine endpos(u)endpos(u). So by the Myhill-Nerode theorem the automaton is minimal. (For the language of all substrings, two strings are right-equivalent as soon as their first occurrences end at the same place, so that minimal automaton can have fewer states.) □\square

The running time. Let d(v)d(v) be the number of links from vv to state 0. First, for each transition δ(x,a)=y\delta(x, a) = y, d(y)≤d(x)+1d(y) \le d(x) + 1: fix w∈xw \in x; each state z≠0z \ne 0 on the link path of yy contains a suffix u′au'a of wawa, and u′u' is a suffix of ww, so [u′][u'] is on the link path of xx; two different zz give different classes [u′][u'] by (F2); so the path of yy has at most one more state than that of xx.

Consider a call with k2k_2 iterations of line L2 and k6k_6 of line L6. The state pp after line L2 has d(p)=d(last)−k2d(p) = d(last) - k_2, and its path is not changed by the call, since only qq and the new states get new links and len(q)>len(p)len(q) > len(p). In case L3, d(cur)=1d(cur) = 1. In case L4, d(cur)=d(q)+1≤d(p)+2d(cur) = d(q) + 1 \le d(p) + 2. In the clone case, the last state redirected in line L6 has depth d(p)−k6+1d(p) - k_6 + 1 and a cc-transition to the clone, so d(cur)=d(clone)+1≤d(p)−k6+3d(cur) = d(clone) + 1 \le d(p) - k_6 + 3. In all cases d(cur)≤d(last)−k2−k6+3d(cur) \le d(last) - k_2 - k_6 + 3. Summing over the nn calls, with d≥0d \ge 0, the total number of iterations of lines L2 and L6 is at most 3n3n. With the at most 3n−43n - 4 transitions copied in line L5, the number of transition operations is O(n)O(n), and the time is O(n)O(n) times the cost of one transition operation. ■\blacksquare

Back to Blumer et al. suffix automaton algorithm