Algorithm Search⌘K

Proofs: Sliding window for longest distinct substring

These are the human proofs of Sliding window for longest distinct substring, at full length.

The sliding window returns the leftmost longest substring without repeated characters full

This proof shows: correctness, termination.

We use the pseudocode of the page. The string is s=s0⋯sn−1s = s_0 \cdots s_{n-1}. A substring s[i…j]s[i \ldots j] with i≤ji \le j is repeat-free if its j−i+1j - i + 1 characters are distinct. For 0≤r≤n−10 \le r \le n - 1 let L(r)L(r) be the smallest i≤ri \le r such that s[i…r]s[i \ldots r] is repeat-free; it exists since s[r…r]s[r \ldots r] is repeat-free. Let p(r)p(r) be the largest j<rj < r with sj=srs_j = s_r, or −1-1.

Lemma 1 (closed under sub-intervals). If s[i…j]s[i \ldots j] is repeat-free and i≤i′≤j′≤ji \le i' \le j' \le j, then s[i′…j′]s[i' \ldots j'] is repeat-free. So for each rr, s[i…r]s[i \ldots r] is repeat-free if and only if L(r)≤i≤rL(r) \le i \le r.

Proof. The characters of s[i′…j′]s[i' \ldots j'] are a subset of those of s[i…j]s[i \ldots j], at distinct positions. For the second part, if s[L(r)…r]s[L(r) \ldots r] is repeat-free, so is each s[i…r]s[i \ldots r] with i≥L(r)i \ge L(r); and s[i…r]s[i \ldots r] with i<L(r)i < L(r) is not repeat-free by the choice of L(r)L(r). ■\blacksquare

Lemma 2. L(0)=0L(0) = 0, and L(r)=max⁡(L(r−1),p(r)+1)L(r) = \max(L(r-1), p(r) + 1) for r≥1r \ge 1.

Proof. For i≤r−1i \le r - 1, the window s[i…r]s[i \ldots r] is repeat-free if and only if s[i…r−1]s[i \ldots r-1] is repeat-free and no jj with i≤j≤r−1i \le j \le r - 1 has sj=srs_j = s_r. By Lemma 1 the first condition is i≥L(r−1)i \ge L(r-1). The second is i>p(r)i > p(r), since p(r)p(r) is the last earlier copy of srs_r. The window s[r…r]s[r \ldots r] is always repeat-free, and max⁡(L(r−1),p(r)+1)≤r\max(L(r-1), p(r) + 1) \le r because L(r−1)≤r−1L(r-1) \le r - 1 and p(r)≤r−1p(r) \le r - 1. So the smallest valid ii is max⁡(L(r−1),p(r)+1)\max(L(r-1), p(r) + 1). ■\blacksquare

Claim. After step rr of the loop of line L2:

(U) l=L(r)l = L(r); last[c]last[c] is the largest j≤rj \le r with sj=cs_j = c, or −1-1 if there is none, for every character cc; best=max⁡r′≤r(r′−L(r′)+1)best = \max_{r' \le r} (r' - L(r') + 1); and startstart is the smallest ii such that s[i…i+best−1]s[i \ldots i + best - 1] is repeat-free and i+best−1≤ri + best - 1 \le r.

On return, bestbest is the length of a longest repeat-free substring of ss, and startstart is the smallest start of one; for n=0n = 0 the result is (0,0)(0, 0).

Proof. By induction on rr. Before step 0, by line L1, l=0l = 0, best=0best = 0, start=0start = 0 and last[c]=−1last[c] = -1 for all cc.

Line L3. Before line L4 of step rr, last[sr]last[s_r] is the largest j≤r−1j \le r - 1 with sj=srs_j = s_r, which is p(r)p(r) (for r=0r = 0, it is −1=p(0)-1 = p(0)). The value of ll before line L3 is L(r−1)L(r-1) for r≥1r \ge 1, and 0 for r=0r = 0. So line L3 sets ll to max⁡(L(r−1),p(r)+1)=L(r)\max(L(r-1), p(r) + 1) = L(r) by Lemma 2, and to max⁡(0,0)=0=L(0)\max(0, 0) = 0 = L(0) for r=0r = 0.

Line L4. It sets last[sr]=rlast[s_r] = r, the largest j≤rj \le r with sj=srs_j = s_r. The other entries are unchanged and stay correct.

Line L5. The longest repeat-free substring that ends at rr is s[L(r)…r]s[L(r) \ldots r], by Lemma 1, of length r−L(r)+1r - L(r) + 1. If this is larger than bestbest, line L5 sets bestbest to it and startstart to L(r)L(r). The new bestbest is max⁡r′≤r(r′−L(r′)+1)\max_{r' \le r}(r' - L(r') + 1). A repeat-free substring of the new length that ends at r′≤rr' \le r must have r′=rr' = r, since all earlier ones were shorter; and the only one that ends at rr with this length starts at L(r)L(r). So startstart is the smallest start. If the length is not larger, bestbest and startstart stay. The new best substrings that end at rr, if any, start at L(r)=r−best+1L(r) = r - best + 1, which is larger than the old startstart; so startstart stays the smallest start.

Result. Every repeat-free substring s[i…j]s[i \ldots j] has i≥L(j)i \ge L(j) by Lemma 1, so its length is at most j−L(j)+1≤bestj - L(j) + 1 \le best after step n−1n - 1. And s[start…start+best−1]s[start \ldots start + best - 1] is repeat-free, by (U). For n=0n = 0 the loop does not run and line L6 returns (0,0)(0, 0). ■\blacksquare

No skipped answer. The left end only increases, by Lemma 2 and line L3. A start i<L(r)i < L(r) that the left end has passed gives a window s[i…r]s[i \ldots r] with a repeat, and every window s[i…r′]s[i \ldots r'] with r′≥rr' \ge r contains s[i…r]s[i \ldots r], so it has the same repeat by Lemma 1. So no repeat-free window with start ii and end at or after rr exists, and passing ii loses no candidate.

Termination and cost. The loop of line L2 runs nn times, and each iteration does O(1)O(1) table operations. Line L1 initializes a table of size σ\sigma. The total time is Θ(n+σ)\Theta(n + \sigma). ■\blacksquare

Back to Sliding window for longest distinct substring