Algorithm Search⌘K

Proofs: Suffix automaton

These are the human proofs of Suffix automaton, at full length.

SAM-EXTEND keeps the end-position classes, transitions and suffix links full

This proof shows: correctness.

Let S=S[0…n−1]S = S[0 \ldots n-1]. For a string xx, endposS(x)={ e:0≤e<n, S[e−∣x∣+1…e]=x }endpos_S(x) = \{\, e : 0 \le e < n,\ S[e - |x| + 1 \ldots e] = x \,\} for x≠εx \ne \varepsilon, and endposS(ε)={−1,0,…,n−1}endpos_S(\varepsilon) = \{-1, 0, \ldots, n-1\}. Write x≡Syx \equiv_S y if endposS(x)=endposS(y)≠∅endpos_S(x) = endpos_S(y) \ne \emptyset; the classes are the substrings of SS grouped by endposendpos.

Lemma 1. For substrings x,yx, y with ∣x∣≤∣y∣|x| \le |y|: if xx is a suffix of yy then endpos(y)⊆endpos(x)endpos(y) \subseteq endpos(x); otherwise endpos(x)∩endpos(y)=∅endpos(x) \cap endpos(y) = \emptyset. Proof. If ee is in both, then xx and yy both end at ee, so the shorter is a suffix of the longer. □\square

Lemma 2 (classes are suffix intervals). A class C≠[ε]C \ne [\varepsilon] has a longest string long(C)long(C), and CC is the set of suffixes of long(C)long(C) with lengths in an interval [minlen(C),∣long(C)∣][minlen(C), |long(C)|]. Proof. By Lemma 1, all members of CC are suffixes of the longest one. If xx and zz are in CC and yy is a suffix of zz longer than xx, then endpos(z)⊆endpos(y)⊆endpos(x)=endpos(z)endpos(z) \subseteq endpos(y) \subseteq endpos(x) = endpos(z) by Lemma 1, so y∈Cy \in C. □\square

Define link(C)link(C) as the class of the suffix of long(C)long(C) of length minlen(C)−1minlen(C) - 1; by Lemma 2 it is the class of the longest suffix of the strings of CC that is not in CC, and len(link(C))=minlen(C)−1len(link(C)) = minlen(C) - 1.

Lemma 3 (transitions are well defined). If x≡yx \equiv y and xcxc is a substring, then ycyc is a substring and xc≡ycxc \equiv yc, since endpos(xc)={ e+1:e∈endpos(x), S[e+1]=c }endpos(xc) = \{\, e + 1 : e \in endpos(x),\ S[e+1] = c \,\}. □\square

So the automaton with one state per class, transitions [x]→c[xc][x] \xrightarrow{c} [xc], and accepting states the classes that contain a suffix of SS is deterministic and accepts exactly the suffixes of SS (reading xx from the initial state [ε][\varepsilon] succeeds exactly when xx is a substring, and ends in [x][x]). It is minimal: two substrings x≢yx \not\equiv y have, say, e∈endpos(x)∖endpos(y)e \in endpos(x) \setminus endpos(y), and then x⋅S[e+1…n−1]x \cdot S[e+1 \ldots n-1] is a suffix of SS while y⋅S[e+1…n−1]y \cdot S[e+1 \ldots n-1] is not, so the states are distinguishable (Myhill-Nerode), and all non-substrings go to one dead state that is not stored.

The claim. After each call, the stored states, with lenlen, linklink and nextnext, are exactly the classes of the current text with len(v)=∣long(v)∣len(v) = |long(v)|, linklink as defined, and transitions as in Lemma 3; and lastlast is the class of the whole text. This holds for S=εS = \varepsilon: one class {ε}\{\varepsilon\}, no transitions.

One step. Assume the claim for SS, of length nn, and let S′=ScS' = Sc. For each substring xx of S′S',

endposS′(x)=endposS(x)∪{n} if x is a suffix of S′,endposS′(x)=endposS(x) otherwise,endpos_{S'}(x) = endpos_S(x) \cup \{n\} \text{ if } x \text{ is a suffix of } S', \quad endpos_{S'}(x) = endpos_S(x) \text{ otherwise,}

with endposS(x)=∅endpos_S(x) = \emptyset when xx is not a substring of SS. The suffixes of S′S' are ε\varepsilon and ycyc for the suffixes yy of SS. By the claim and Lemma 2, the suffixes of SS are the strings of the states on the link path last=p0,p1=link(p0),…,pk=initlast = p_0, p_1 = link(p_0), \ldots, p_k = init, with lengths decreasing along the path, and every length from nn down to 0 appears once.

Line L1. curcur will be the class of S′S', with len=n+1len = n + 1.

Line L3. Let p0,…,pt−1p_0, \ldots, p_{t-1} be the states without a transition on cc. For yy in such a state, ycyc is not a substring of SS (by Lemma 3 and the claim), so endposS′(yc)={n}=endposS′(S′)endpos_{S'}(yc) = \{n\} = endpos_{S'}(S'), and ycyc belongs to the class of curcur. Line L3 sets next(pi)[c]=curnext(p_i)[c] = cur, which is right by Lemma 3.

Line L4, case $p_t = $ NIL. Then no ycyc occurred in SS, so cc does not occur in SS; the class of curcur holds all nonempty suffixes of S′S', its minlenminlen is 1, and its link is initinit. No old class changes: the only old strings whose endposendpos changes are suffixes of S′S', and only ε\varepsilon is old among them, whose class is always {ε}\{\varepsilon\}.

Otherwise, let p=ptp = p_t, q=next(p)[c]q = next(p)[c], and y0=long(p)y_0 = long(p). The strings ycyc for yy in pp are in qq (Lemma 3), and every shorter suffix ycyc is a substring of SS (a suffix of y0cy_0 c). So the class of curcur holds exactly the suffixes of S′S' longer than len(p)+1len(p) + 1, its minlenminlen is len(p)+2len(p) + 2, and its link must be the class in S′S' of y0cy_0 c.

Case len(q)=len(p)+1len(q) = len(p) + 1 (line L5). Then long(q)=y0clong(q) = y_0 c, a suffix of S′S'; by Lemma 2 every string of qq is a suffix of y0cy_0 c and so of S′S', and they all gain nn. So qq remains one class, unchanged in its strings, and link(cur)=qlink(cur) = q. Every other old class either consists of suffixes of S′S' (those on the link path of qq, whose strings are suffixes of y0cy_0 c; they all gain nn and stay classes) or of non-suffixes (unchanged). Transitions out of old classes into curcur are exactly those set in line L3, since xc′xc' for an old xx lies in curcur only if it is a suffix of S′S' longer than len(p)+1len(p) + 1, which means c′=cc' = c and xx in p0,…,pt−1p_0, \ldots, p_{t-1}.

Case len(q)>len(p)+1len(q) > len(p) + 1 (lines L6 to L8). The strings of qq with length at most len(p)+1len(p) + 1 are suffixes of y0cy_0 c, hence suffixes of S′S', and gain nn. The strings of qq with length greater than len(p)+1len(p) + 1 are not suffixes of S′S': such a string zczc, being in qq, has zz in a state with a transition to qq, and if zczc were a suffix of S′S', zz would be a suffix of SS longer than y0y_0 with a transition on cc, so it would be in one of p0,…,pt−1p_0, \ldots, p_{t-1}, which have no such transition. So qq splits into qshortq_{short}, the strings of lengths minlen(q)minlen(q) to len(p)+1len(p) + 1, and qlongq_{long}, the rest. The clone is qshortq_{short}, with len=len(p)+1len = len(p) + 1 and the old link of qq; qq becomes qlongq_{long}, with minlen=len(p)+2minlen = len(p) + 2, so its link is the clone; and link(cur)link(cur) is the clone, the class of y0cy_0 c. Both parts take the transitions of qq as they are after line L3, which line L6 copies: by Lemma 3 in S′S', applied to each part, the extensions wc′wc' of the strings ww of one part lie in one class, and that class contains the old target of the transition of qq on c′c' (an old class or curcur); the only class that is split in this step is qq itself, and transitions into qq are handled next. An incoming transition x→c′qx \xrightarrow{c'} q must now go to the part that contains xc′xc'. If c′≠cc' \ne c or xx is not a suffix of SS, then xc′xc' is not a suffix of S′S', so it is in qlongq_{long}. If c′=cc' = c and xx is a suffix of SS, then xx is in one of pt,pt+1,…p_t, p_{t+1}, \ldots, and xcxc is in qshortq_{short} exactly when it goes to qq: along the link path from ptp_t, the states whose transition on cc is qq form a prefix pt,…,ps−1p_t, \ldots, p_{s-1} (a shorter xx has xcxc shorter, so once xcxc falls below minlen(q)minlen(q) it is in a class of the link path of qq, never again in qq). Line L7 redirects exactly these to the clone. All other old classes are unchanged in their strings, by the same argument as in the first case.

Line L9. last=curlast = cur, the class of S′S'.

So the claim holds for S′S'. Taking all states as accepting gives the substrings of SS, and the accepting states of the suffix automaton are those on the link path of lastlast, as stated in (A4). ■\blacksquare

Back to Suffix automaton