Commentz-Walter outputs exactly the occurrences of the patterns full
This proof shows: correctness, termination.
Claim. For every text T[0…n−1] and patterns P0,…,Pk−1, COMMENTZ-WALTER stops and outputs each pair (s,j) with 0≤s≤n−∣Pj∣ and T[s…s+∣Pj∣−1]=Pj exactly once, and no other pair.
Notation. A pattern suffix is a suffix of a nonempty pattern, the empty string included. Each node v of the trie of line L3 stands for a pattern suffix y(v): the labels on the path from the root to v spell y(v) backward, and d(v)=∣y(v)∣. A child u of v by the character c has y(u)=cy(v). The list out[v] holds the j with Pj=y(v), and v is a pattern node if this list is not empty. The failure link fail(v), for v= root, is the node of the longest proper prefix of y(v) that is a pattern suffix; it is the Aho-Corasick failure link in the trie of the reversed patterns. The chain of v′ is the sequence fail(v′),fail(fail(v′)),…, down to the root. A pattern P ends at e if it occurs at e−∣P∣+1. Let w be the shortest nonempty pattern length.
Lemma 1 (the trie). Every pattern suffix is y(v) for exactly one node v, and every suffix of a pattern suffix is a pattern suffix.
Proof. The trie of the reversed nonempty patterns has one node for each prefix of a reversed pattern, that is, for each reversed pattern suffix. A suffix of a suffix of P is a suffix of P. □
Lemma 2 (the reading). Fix a window end pos. Lines L9 to L12 visit the nodes vt with y(vt)=T[pos−t+1…pos] for t=0,1,…,D, where D is the largest t≤pos+1 for which T[pos−t+1…pos] is a pattern suffix. They output (pos−∣Pj∣+1,j) for exactly the nonempty patterns Pj that end at pos, each once.
Proof. By induction on t: at t=0 the node is the root, with y=ε. If the node is vt and i=pos−t≥0, then vt has a child for T[i] if and only if T[i]y(vt)=T[i…pos] is a pattern suffix (Lemma 1), and that child is vt+1. By Lemma 1 the strings T[pos−t+1…pos] that are pattern suffixes are exactly those with t≤D, so the loop visits v0,…,vD and stops. At vt, t≥1, line L12 outputs (i+1,j)=(pos−t+1,j) for each j with Pj=T[pos−t+1…pos]. A nonempty Pj ends at pos if and only if ∣Pj∣≤pos+1 and Pj=T[pos−∣Pj∣+1…pos]; then this string is a pattern suffix, so ∣Pj∣≤D, and Pj is reported at t=∣Pj∣, at that visit only. □
Lemma 3 (chains). A node v is on the chain of v′ if and only if y(v) is a proper prefix of y(v′).
Proof. Each step of the chain goes to a proper prefix, and a proper prefix of a prefix is a prefix, so every chain node spells a proper prefix of y(v′). Conversely, let x be a proper prefix of y(v′) that is a pattern suffix. We use induction on d(v′). By definition ∣y(fail(v′))∣≥∣x∣. If they are equal, x=y(fail(v′)). Otherwise x is a proper prefix of y(fail(v′)), because both are prefixes of y(v′), and by induction x is spelled by a node on the chain of fail(v′), which is part of the chain of v′. □
So, for a node v, the nodes v′ whose chain contains v are the pattern suffixes y(v)x with ∣x∣=d(v′)−d(v)≥1.
Lemma 4 (the tables). After lines L4 to L6:
- char(c)=min({w+1}∪{1+e:c=Pj[∣Pj∣−1−e] for some j and 0≤e<∣Pj∣}).
- shift1(v)=min({w}∪{∣x∣:∣x∣≥1 and y(v)x is a pattern suffix}).
- shift2(v)=min({w}∪{∣Pj∣−i:1≤i<∣Pj∣ and Pj[0…i−1] is a suffix of y(v)}).
Proof. (1) A node u entered by c has y(u)=cy(parent(u)), so c occurs in a pattern at distance e=d(u)−1 from its end, and d(u)=1+e. Conversely, an occurrence of c at distance e from the end of Pj gives the pattern suffix cPj[∣Pj∣−e…∣Pj∣−1], whose node is entered by c and has depth 1+e.
(2) By Lemma 3 and the remark after it, the set in (2) is {d(v′)−d(v):v is on the chain of v′}. Line L5 takes the minimum over the v′ with fail(v′)=v only. This loses nothing: if v is on the chain of v′, the chain node u just before v (or u=v′) has fail(u)=v and d(u)≤d(v′).
(3) Let g(v) be the minimum of d(v′)−d(v) over the pattern nodes v′ whose chain contains v (∞ if none). By Lemma 3 this is the minimum of ∣x∣≥1 such that y(v)x is a pattern. The pass in decreasing depth computes it: for such a v′, let u be the chain node just before v (or u=v′), so fail(u)=v. If u=v′, u passes d(u)−d(v), which is at most the value of u as a pattern node. If u=v′, then u is on the chain of v′, so d(v′)−d(u)≥g(u), and u passes at most g(u)+d(u)−d(v)≤d(v′)−d(v). Conversely each value passed is d(v′)−d(v) for some pattern node v′ whose chain contains v. Every node that passes to u is deeper than u, so g(u) is final when u passes it on.
Now the set in (3) for the root has no element, since i≥1, so shift2(root)=w. For v= root with parent q, y(v)=cy(q). A suffix of y(v) of length i≥1 is y(v) itself or a suffix of y(q). The terms with i=d(v) are those with Pj=y(v)x, ∣x∣=∣Pj∣−i≥1, whose minimum is g(v). The terms with i<d(v) form the set for q. So the minimum is min(shift2(q),g(v)), which line L6 computes from the root down. □
Lemma 5 (safe shift). Let the reading at pos stop at v with i=pos−d(v), and let c be as in line L13. Then σ=min(max(shift1(v),char(c)−d(v)−1),shift2(v))≥1, and no nonempty pattern ends at any e with pos<e<pos+σ.
Proof. Each element of the sets in Lemma 4 (2) and (3) is at least 1, and w≥1, so shift1(v)≥1 and shift2(v)≥1, and σ≥1. Let a nonempty pattern P end at e=pos+δ with δ≥1, and let s=e−∣P∣+1 be its start. By Lemma 2, y(v)=T[i+1…pos]. We show δ≥σ.
Case A: s≤i. Then i≥s≥0, and the reading stopped because v has no child for c=T[i]. The occurrence contains T[i…e], so y(v)x with x=T[pos+1…e] is a suffix of P, and ∣x∣=δ≥1. By Lemma 4 (2), δ≥shift1(v). Also c=T[i] occurs in P at distance e−i=δ+d(v) from its end, so by Lemma 4 (1), char(c)≤1+δ+d(v), that is, δ≥char(c)−d(v)−1. So δ≥max(shift1(v),char(c)−d(v)−1)≥σ.
Case B: i<s≤pos. Let i′=pos−s+1. Then 1≤i′≤d(v), and i′<∣P∣ because e>pos. The prefix P[0…i′−1]=T[s…pos] is a suffix of y(v), and ∣P∣−i′=e−pos=δ. By Lemma 4 (3), δ≥shift2(v)≥σ.
Case C: s>pos. Then δ=e−pos≥e−s+1=∣P∣≥w≥shift2(v)≥σ.
The cases cover all s. When i<0, case A cannot occur because s≥0, so the value given to c in line L13 does not matter. □
Theorem. The claim holds.
Proof. Line L1 outputs the pairs of the empty patterns, each (s,j) with 0≤s≤n once, which are exactly their occurrences. If no pattern is nonempty, line L2 stops and the output is complete. Otherwise let pos0=w−1<pos1<⋯ be the window ends of line L8; they increase by at least 1 (Lemma 5), so there are at most n−w+1 of them, and each inner loop stops because i decreases. So the algorithm stops. A nonempty pattern P ends at some e only if w−1≤∣P∣−1≤e≤n−1. By Lemma 5, no end lies strictly between two consecutive window ends, and none lies between the last window end ≤n−1 and n−1, because the next window end is above n−1. So every e at which a nonempty pattern ends is a window end, and by Lemma 2 the reading at e outputs every pair (s,j) with Pj ending at e. Each window end is visited once and each reading outputs each such pair once, and no reading outputs anything else, so each pair is output exactly once and no other pair is output. ■