Algorithm Search⌘K

Proofs: Commentz-Walter algorithm

These are the human proofs of Commentz-Walter algorithm, at full length.

Commentz-Walter outputs exactly the occurrences of the patterns full

This proof shows: correctness, termination.

Claim. For every text T[0…n−1]T[0 \ldots n-1] and patterns P0,…,Pk−1P_0, \ldots, P_{k-1}, COMMENTZ-WALTER stops and outputs each pair (s,j)(s, j) with 0≤s≤n−∣Pj∣0 \le s \le n - |P_j| and T[s…s+∣Pj∣−1]=PjT[s \ldots s + |P_j| - 1] = P_j exactly once, and no other pair.

Notation. A pattern suffix is a suffix of a nonempty pattern, the empty string included. Each node vv of the trie of line L3 stands for a pattern suffix y(v)y(v): the labels on the path from the root to vv spell y(v)y(v) backward, and d(v)=∣y(v)∣d(v) = |y(v)|. A child uu of vv by the character cc has y(u)=c y(v)y(u) = c\, y(v). The list out[v]\mathrm{out}[v] holds the jj with Pj=y(v)P_j = y(v), and vv is a pattern node if this list is not empty. The failure link fail(v)\mathrm{fail}(v), for v≠v \ne root, is the node of the longest proper prefix of y(v)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′v' is the sequence fail(v′),fail(fail(v′)),…\mathrm{fail}(v'), \mathrm{fail}(\mathrm{fail}(v')), \ldots, down to the root. A pattern PP ends at ee if it occurs at e−∣P∣+1e - |P| + 1. Let ww be the shortest nonempty pattern length.

Lemma 1 (the trie). Every pattern suffix is y(v)y(v) for exactly one node vv, 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 PP is a suffix of PP. □\square

Lemma 2 (the reading). Fix a window end pos\mathrm{pos}. Lines L9 to L12 visit the nodes vtv_t with y(vt)=T[pos−t+1…pos]y(v_t) = T[\mathrm{pos} - t + 1 \ldots \mathrm{pos}] for t=0,1,…,Dt = 0, 1, \ldots, D, where DD is the largest t≤pos+1t \le \mathrm{pos} + 1 for which T[pos−t+1…pos]T[\mathrm{pos} - t + 1 \ldots \mathrm{pos}] is a pattern suffix. They output (pos−∣Pj∣+1,j)(\mathrm{pos} - |P_j| + 1, j) for exactly the nonempty patterns PjP_j that end at pos\mathrm{pos}, each once.

Proof. By induction on tt: at t=0t = 0 the node is the root, with y=εy = \varepsilon. If the node is vtv_t and i=pos−t≥0i = \mathrm{pos} - t \ge 0, then vtv_t has a child for T[i]T[i] if and only if T[i] y(vt)=T[i…pos]T[i]\, y(v_t) = T[i \ldots \mathrm{pos}] is a pattern suffix (Lemma 1), and that child is vt+1v_{t+1}. By Lemma 1 the strings T[pos−t+1…pos]T[\mathrm{pos} - t + 1 \ldots \mathrm{pos}] that are pattern suffixes are exactly those with t≤Dt \le D, so the loop visits v0,…,vDv_0, \ldots, v_D and stops. At vtv_t, t≥1t \ge 1, line L12 outputs (i+1,j)=(pos−t+1,j)(i + 1, j) = (\mathrm{pos} - t + 1, j) for each jj with Pj=T[pos−t+1…pos]P_j = T[\mathrm{pos} - t + 1 \ldots \mathrm{pos}]. A nonempty PjP_j ends at pos\mathrm{pos} if and only if ∣Pj∣≤pos+1|P_j| \le \mathrm{pos} + 1 and Pj=T[pos−∣Pj∣+1…pos]P_j = T[\mathrm{pos} - |P_j| + 1 \ldots \mathrm{pos}]; then this string is a pattern suffix, so ∣Pj∣≤D|P_j| \le D, and PjP_j is reported at t=∣Pj∣t = |P_j|, at that visit only. □\square

Lemma 3 (chains). A node vv is on the chain of v′v' if and only if y(v)y(v) is a proper prefix of y(v′)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′)y(v'). Conversely, let xx be a proper prefix of y(v′)y(v') that is a pattern suffix. We use induction on d(v′)d(v'). By definition ∣y(fail(v′))∣≥∣x∣|y(\mathrm{fail}(v'))| \ge |x|. If they are equal, x=y(fail(v′))x = y(\mathrm{fail}(v')). Otherwise xx is a proper prefix of y(fail(v′))y(\mathrm{fail}(v')), because both are prefixes of y(v′)y(v'), and by induction xx is spelled by a node on the chain of fail(v′)\mathrm{fail}(v'), which is part of the chain of v′v'. □\square

So, for a node vv, the nodes v′v' whose chain contains vv are the pattern suffixes y(v) xy(v)\, x with ∣x∣=d(v′)−d(v)≥1|x| = d(v') - d(v) \ge 1.

Lemma 4 (the tables). After lines L4 to L6:

  1. char(c)=min⁡({w+1}∪{1+e:c=Pj[∣Pj∣−1−e] for some j and 0≤e<∣Pj∣})\mathrm{char}(c) = \min(\{w + 1\} \cup \{1 + e : c = P_j[|P_j| - 1 - e] \text{ for some } j \text{ and } 0 \le e < |P_j|\}).
  2. shift1(v)=min⁡({w}∪{∣x∣:∣x∣≥1 and y(v) x is a pattern suffix})\mathrm{shift1}(v) = \min(\{w\} \cup \{|x| : |x| \ge 1 \text{ and } y(v)\, x \text{ is a pattern suffix}\}).
  3. shift2(v)=min⁡({w}∪{∣Pj∣−i:1≤i<∣Pj∣ and Pj[0…i−1] is a suffix of y(v)})\mathrm{shift2}(v) = \min(\{w\} \cup \{|P_j| - i : 1 \le i < |P_j| \text{ and } P_j[0 \ldots i-1] \text{ is a suffix of } y(v)\}).

Proof. (1) A node uu entered by cc has y(u)=c y(parent(u))y(u) = c\, y(\mathrm{parent}(u)), so cc occurs in a pattern at distance e=d(u)−1e = d(u) - 1 from its end, and d(u)=1+ed(u) = 1 + e. Conversely, an occurrence of cc at distance ee from the end of PjP_j gives the pattern suffix c Pj[∣Pj∣−e…∣Pj∣−1]c\, P_j[|P_j| - e \ldots |P_j| - 1], whose node is entered by cc and has depth 1+e1 + 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′}\{d(v') - d(v) : v \text{ is on the chain of } v'\}. Line L5 takes the minimum over the v′v' with fail(v′)=v\mathrm{fail}(v') = v only. This loses nothing: if vv is on the chain of v′v', the chain node uu just before vv (or u=v′u = v') has fail(u)=v\mathrm{fail}(u) = v and d(u)≤d(v′)d(u) \le d(v').

(3) Let g(v)g(v) be the minimum of d(v′)−d(v)d(v') - d(v) over the pattern nodes v′v' whose chain contains vv (∞\infty if none). By Lemma 3 this is the minimum of ∣x∣≥1|x| \ge 1 such that y(v) xy(v)\, x is a pattern. The pass in decreasing depth computes it: for such a v′v', let uu be the chain node just before vv (or u=v′u = v'), so fail(u)=v\mathrm{fail}(u) = v. If u=v′u = v', uu passes d(u)−d(v)d(u) - d(v), which is at most the value of uu as a pattern node. If u≠v′u \ne v', then uu is on the chain of v′v', so d(v′)−d(u)≥g(u)d(v') - d(u) \ge g(u), and uu passes at most g(u)+d(u)−d(v)≤d(v′)−d(v)g(u) + d(u) - d(v) \le d(v') - d(v). Conversely each value passed is d(v′)−d(v)d(v') - d(v) for some pattern node v′v' whose chain contains vv. Every node that passes to uu is deeper than uu, so g(u)g(u) is final when uu passes it on.

Now the set in (3) for the root has no element, since i≥1i \ge 1, so shift2(root)=w\mathrm{shift2}(\mathrm{root}) = w. For v≠v \ne root with parent qq, y(v)=c y(q)y(v) = c\, y(q). A suffix of y(v)y(v) of length i≥1i \ge 1 is y(v)y(v) itself or a suffix of y(q)y(q). The terms with i=d(v)i = d(v) are those with Pj=y(v) xP_j = y(v)\, x, ∣x∣=∣Pj∣−i≥1|x| = |P_j| - i \ge 1, whose minimum is g(v)g(v). The terms with i<d(v)i < d(v) form the set for qq. So the minimum is min⁡(shift2(q),g(v))\min(\mathrm{shift2}(q), g(v)), which line L6 computes from the root down. □\square

Lemma 5 (safe shift). Let the reading at pos\mathrm{pos} stop at vv with i=pos−d(v)i = \mathrm{pos} - d(v), and let cc be as in line L13. Then σ=min⁡(max⁡(shift1(v),char(c)−d(v)−1),shift2(v))≥1\sigma = \min(\max(\mathrm{shift1}(v), \mathrm{char}(c) - d(v) - 1), \mathrm{shift2}(v)) \ge 1, and no nonempty pattern ends at any ee with pos<e<pos+σ\mathrm{pos} < e < \mathrm{pos} + \sigma.

Proof. Each element of the sets in Lemma 4 (2) and (3) is at least 1, and w≥1w \ge 1, so shift1(v)≥1\mathrm{shift1}(v) \ge 1 and shift2(v)≥1\mathrm{shift2}(v) \ge 1, and σ≥1\sigma \ge 1. Let a nonempty pattern PP end at e=pos+δe = \mathrm{pos} + \delta with δ≥1\delta \ge 1, and let s=e−∣P∣+1s = e - |P| + 1 be its start. By Lemma 2, y(v)=T[i+1…pos]y(v) = T[i + 1 \ldots \mathrm{pos}]. We show δ≥σ\delta \ge \sigma.

Case A: s≤is \le i. Then i≥s≥0i \ge s \ge 0, and the reading stopped because vv has no child for c=T[i]c = T[i]. The occurrence contains T[i…e]T[i \ldots e], so y(v) xy(v)\, x with x=T[pos+1…e]x = T[\mathrm{pos} + 1 \ldots e] is a suffix of PP, and ∣x∣=δ≥1|x| = \delta \ge 1. By Lemma 4 (2), δ≥shift1(v)\delta \ge \mathrm{shift1}(v). Also c=T[i]c = T[i] occurs in PP at distance e−i=δ+d(v)e - i = \delta + d(v) from its end, so by Lemma 4 (1), char(c)≤1+δ+d(v)\mathrm{char}(c) \le 1 + \delta + d(v), that is, δ≥char(c)−d(v)−1\delta \ge \mathrm{char}(c) - d(v) - 1. So δ≥max⁡(shift1(v),char(c)−d(v)−1)≥σ\delta \ge \max(\mathrm{shift1}(v), \mathrm{char}(c) - d(v) - 1) \ge \sigma.

Case B: i<s≤posi < s \le \mathrm{pos}. Let i′=pos−s+1i' = \mathrm{pos} - s + 1. Then 1≤i′≤d(v)1 \le i' \le d(v), and i′<∣P∣i' < |P| because e>pose > \mathrm{pos}. The prefix P[0…i′−1]=T[s…pos]P[0 \ldots i' - 1] = T[s \ldots \mathrm{pos}] is a suffix of y(v)y(v), and ∣P∣−i′=e−pos=δ|P| - i' = e - \mathrm{pos} = \delta. By Lemma 4 (3), δ≥shift2(v)≥σ\delta \ge \mathrm{shift2}(v) \ge \sigma.

Case C: s>poss > \mathrm{pos}. Then δ=e−pos≥e−s+1=∣P∣≥w≥shift2(v)≥σ\delta = e - \mathrm{pos} \ge e - s + 1 = |P| \ge w \ge \mathrm{shift2}(v) \ge \sigma.

The cases cover all ss. When i<0i < 0, case A cannot occur because s≥0s \ge 0, so the value given to cc in line L13 does not matter. □\square

Theorem. The claim holds.

Proof. Line L1 outputs the pairs of the empty patterns, each (s,j)(s, j) with 0≤s≤n0 \le s \le 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<⋯\mathrm{pos}_0 = w - 1 < \mathrm{pos}_1 < \cdots be the window ends of line L8; they increase by at least 1 (Lemma 5), so there are at most n−w+1n - w + 1 of them, and each inner loop stops because ii decreases. So the algorithm stops. A nonempty pattern PP ends at some ee only if w−1≤∣P∣−1≤e≤n−1w - 1 \le |P| - 1 \le e \le n - 1. By Lemma 5, no end lies strictly between two consecutive window ends, and none lies between the last window end ≤n−1\le n - 1 and n−1n - 1, because the next window end is above n−1n - 1. So every ee at which a nonempty pattern ends is a window end, and by Lemma 2 the reading at ee outputs every pair (s,j)(s, j) with PjP_j ending at ee. 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. ■\blacksquare

Back to Commentz-Walter algorithm