Algorithm Search⌘K

Proofs: Z-algorithm

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

The Z-algorithm computes the Z array full

This proof shows: correctness, termination.

Claim. For a string S[0…N−1]S[0 \ldots N-1], Z-ARRAY(S)(S) stops and returns ZZ with Z[0]=NZ[0] = N and, for 1≤k<N1 \le k < N, Z[k]=lcp⁡(S,S[k…N−1])Z[k] = \operatorname{lcp}(S, S[k \ldots N-1]), the length of the longest common prefix. Z-MATCH(T,P)(T, P) outputs exactly the valid shifts in increasing order.

Termination. The loop of line L2 runs N−1N - 1 times. In the loop of lines L4 and L5, k+Z[k]k + Z[k] increases at each pass and is at most NN, so the loop stops.

Invariant. At the start of the iteration for kk:

(I) Z[j]=lcp⁡(S,S[j…])Z[j] = \operatorname{lcp}(S, S[j \ldots]) for 1≤j<k1 \le j < k; r=max⁡({0}∪{ j+Z[j]:1≤j<k })r = \max(\{0\} \cup \{\, j + Z[j] : 1 \le j < k \,\}); and if r>0r > 0 then 1≤l<k1 \le l < k, r=l+Z[l]r = l + Z[l].

It holds for k=1k = 1 with l=r=0l = r = 0. From (I) with r>0r > 0, S[l…r−1]=S[0…r−l−1]S[l \ldots r-1] = S[0 \ldots r-l-1] by the definition of Z[l]Z[l].

Lemma. Let z∗=lcp⁡(S,S[k…])z^* = \operatorname{lcp}(S, S[k \ldots]) and let z0z_0 be the value set by line L3. Then z0≤z∗z_0 \le z^*, and if z0<r−kz_0 < r - k then z0=z∗z_0 = z^*.

Proof. If k≥rk \ge r, z0=0≤z∗z_0 = 0 \le z^*, and the second part is empty since r−k≤0r - k \le 0. Let k<rk < r, so l<k<rl < k < r, and let k′=k−l≥1k' = k - l \ge 1. The box gives S[k…r−1]=S[k′…r−l−1]S[k \ldots r-1] = S[k' \ldots r-l-1], a string of length r−kr - k.

First, z0≤z∗z_0 \le z^*. Since k′<kk' < k, Z[k′]=lcp⁡(S,S[k′…])Z[k'] = \operatorname{lcp}(S, S[k' \ldots]) by (I). With z0=min⁡(r−k,Z[k′])z_0 = \min(r - k, Z[k']), for i<z0i < z_0 we have S[i]=S[k′+i]S[i] = S[k' + i] (as i<Z[k′]i < Z[k']) and S[k′+i]=S[k+i]S[k' + i] = S[k + i] (as i<r−ki < r - k, inside the box). So S[0…z0−1]=S[k…k+z0−1]S[0 \ldots z_0 - 1] = S[k \ldots k + z_0 - 1].

Second, assume z0<r−kz_0 < r - k, so z0=Z[k′]<r−kz_0 = Z[k'] < r - k. Then k′+Z[k′]<r−l≤Nk' + Z[k'] < r - l \le N, so the character S[k′+Z[k′]]S[k' + Z[k']] exists, and it differs from S[Z[k′]]S[Z[k']] by maximality of Z[k′]Z[k']. Since Z[k′]<r−kZ[k'] < r - k, the box gives S[k+Z[k′]]=S[k′+Z[k′]]≠S[Z[k′]]S[k + Z[k']] = S[k' + Z[k']] \ne S[Z[k']]. So the match at kk stops at Z[k′]Z[k'] and z∗=z0z^* = z_0. □\square

Step. By the lemma, z0≤z∗z_0 \le z^*, and S[0…z0−1]=S[k…k+z0−1]S[0 \ldots z_0 - 1] = S[k \ldots k + z_0 - 1]. The loop of lines L4 and L5 keeps S[0…Z[k]−1]=S[k…k+Z[k]−1]S[0 \ldots Z[k]-1] = S[k \ldots k+Z[k]-1] and stops at the first Z[k]≥z0Z[k] \ge z_0 where k+Z[k]=Nk + Z[k] = N or S[Z[k]]≠S[k+Z[k]]S[Z[k]] \ne S[k + Z[k]], which is z∗z^*. Line L6 updates rr to max⁡(r,k+Z[k])\max(r, k + Z[k]) with l=kl = k when rr changes, so (I) holds for k+1k + 1. At the end, (I) for k=Nk = N gives every value, and Z[0]=NZ[0] = N by line L1.

Matching. Let S=P$TS = P \$ T with ∣S∣=m+1+n|S| = m + 1 + n and m≥1m \ge 1, and let 0≤s≤n−m0 \le s \le n - m. Position m+1+sm + 1 + s holds T[s]T[s]. Because $\$ equals no character of PP or TT, the common prefix of SS and S[m+1+s…]S[m+1+s \ldots] cannot include S[m]=$S[m] = \$ matched against a character of TT, so Z[m+1+s]≤mZ[m+1+s] \le m, and Z[m+1+s]=mZ[m+1+s] = m exactly when T[s…s+m−1]=PT[s \ldots s+m-1] = P. Line L11 visits the shifts in increasing order, and none when m>nm > n. For m=0m = 0, line L9 outputs all shifts 0,…,n0, \ldots, n, which are all valid. ■\blacksquare

Back to Z-algorithm