Algorithm Search⌘K

Proofs: Sliding window for minimum window substring

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

The sliding window returns the leftmost shortest window that contains the pattern full

This proof shows: correctness, termination.

We use the pseudocode of the page, with m≥1m \ge 1. For a substring ww and a character cc, write cntw(c)\mathrm{cnt}_w(c) for the number of copies of cc in ww. A window s[i…j]s[i \ldots j] with i≤ji \le j is valid if cnts[i…j](c)≥cntt(c)\mathrm{cnt}_{s[i \ldots j]}(c) \ge \mathrm{cnt}_t(c) for every cc. For a start ii, let e(i)e(i) be the smallest j≥ij \ge i with s[i…j]s[i \ldots j] valid, or ∞\infty. Write s[i…j]s[i \ldots j] with j<ij < i for the empty window, which is not valid because m≥1m \ge 1.

Lemma 1 (closed under super-windows). If s[i…j]s[i \ldots j] is valid, i′≤ii' \le i and j′≥jj' \ge j, then s[i′…j′]s[i' \ldots j'] is valid. Equivalently, every part of a window that is not valid is not valid.

Proof. Each count in s[i′…j′]s[i' \ldots j'] is at least the same count in s[i…j]s[i \ldots j]. ■\blacksquare

Lemma 2 (the counter). Let need[c]=cntt(c)−cntw(c)need[c] = \mathrm{cnt}_t(c) - \mathrm{cnt}_w(c) for a window ww, and missing=∑cmax⁡(need[c],0)missing = \sum_c \max(need[c], 0). Then ww is valid if and only if missing=0missing = 0. Adding one copy of cc to ww decreases need[c]need[c] by 1 and decreases missingmissing by 1 if and only if need[c]>0need[c] > 0 before the change. Removing one copy of cc increases need[c]need[c] by 1 and increases missingmissing by 1 if and only if need[c]>0need[c] > 0 after the change.

Proof. missing=0missing = 0 if and only if every need[c]≤0need[c] \le 0, which is validity. The term max⁡(need[c],0)\max(need[c], 0) changes by 1 exactly when need[c]need[c] moves between 1 and 0, in either direction, and does not change otherwise. ■\blacksquare

Claim. At the start of each step rr of the loop of line L2, and after the loop with r=nr = n:

(W) 0≤l≤r0 \le l \le r; needneed and missingmissing are the values of Lemma 2 for the window s[l…r−1]s[l \ldots r-1]; this window is not valid; for each start i<li < l, e(i)≤r−1e(i) \le r - 1, and the window s[i…e(i)]s[i \ldots e(i)] was examined at line L6; and (bestlen,beststart)(bestlen, beststart) is the length and start of the shortest examined window, with the smallest start among the shortest, or (∞,0)(\infty, 0) if none was examined.

On return, the output is the leftmost shortest valid window, or the empty string if no window is valid.

Proof. Initialization. Line L1 sets the values of Lemma 2 for the empty window s[0…−1]s[0 \ldots -1], which is not valid, and l=0l = 0, so there is no start i<li < l.

Maintenance. Assume (W) at the start of step rr. Lines L3 and L4 add srs_r to the window, and by Lemma 2 they keep needneed and missingmissing correct for s[l…r]s[l \ldots r]. The loop of line L5 runs while s[l…r]s[l \ldots r] is valid, by Lemma 2. Let l0l_0 be the value of ll before the loop. In each run with left end l≥l0l \ge l_0, the window s[l…r−1]s[l \ldots r-1] is a part of s[l0…r−1]s[l_0 \ldots r-1], which is not valid by (W), so s[l…r−1]s[l \ldots r-1] is not valid by Lemma 1, while s[l…r]s[l \ldots r] is valid. So e(l)=re(l) = r, and line L6 examines the window s[l…e(l)]s[l \ldots e(l)]. Lines L7 to L9 remove sls_l and keep needneed and missingmissing correct for s[l+1…r]s[l+1 \ldots r] by Lemma 2. So after each run, every start ii below the new ll has e(i)≤re(i) \le r and its window was examined. The loop ends when s[l…r]s[l \ldots r] is not valid; it ends because s[r+1…r]s[r+1 \ldots r] is empty and not valid, so l≤r+1l \le r + 1. Line L6 keeps the shortest examined window, and replaces it only by a strictly shorter one; since the windows are examined in increasing order of start, the kept one has the smallest start among the shortest. So (W) holds at the start of step r+1r + 1.

Result. After the last step, (W) holds with r=nr = n. Every start i<li < l has had its window s[i…e(i)]s[i \ldots e(i)] examined. For every start i≥li \ge l, the window s[i…n−1]s[i \ldots n-1] is a part of s[l…n−1]s[l \ldots n-1], which is not valid, so e(i)=∞e(i) = \infty. Every valid window s[i…j]s[i \ldots j] has j≥e(i)j \ge e(i), so it is at least as long as s[i…e(i)]s[i \ldots e(i)], which was examined. So the shortest examined window is a shortest valid window, and its start is the smallest among them: if two valid windows have the minimum length LL and starts i<i′i < i', both are of the form s[i…e(i)]s[i \ldots e(i)], and the one with start ii was examined first. If no window is valid, nothing was examined, bestlen=∞bestlen = \infty, and line L10 returns the empty string. ■\blacksquare

No skipped answer. The left end passes a start ii only after line L6 has examined s[i…e(i)]s[i \ldots e(i)], the shortest valid window with start ii. The right end passes an end rr only when s[l…r]s[l \ldots r] is not valid, and by Lemma 1 no valid window s[i…r]s[i \ldots r] with i≥li \ge l exists; the windows s[i…r]s[i \ldots r] with i<li < l are longer than s[i…e(i)]s[i \ldots e(i)] or equal to it, and that window was examined. So no candidate for the minimum is lost.

Termination and cost. Each run of the loop of line L5 increases ll by 1, and l≤nl \le n always, so the loop of line L5 runs at most nn times in total. The loop of line L2 runs nn times. Each run costs O(1)O(1). Line L1 costs Θ(m+σ)\Theta(m + \sigma). The total is Θ(n+m+σ)\Theta(n + m + \sigma). ■\blacksquare

Back to Sliding window for minimum window substring