The bitap algorithm reports exactly the windows with at most k mismatches full
This proof shows: correctness.
Indices are 0-based, ∣P∣=m≥1, ∣T∣=n, and B=⌈log2(k+1)⌉+1, so 2B−1≥k+1. For a vector V of mB bits, Vi is the value of field i, bits iB to iB+B−1. Each field of H is 2B−1. Each field of M[c] is [P[i]=c], which is 0 or 1. For 0≤i≤j, mm(i,j)=∣{q≤i:P[q]=T[j−i+q]}∣.
Lemma 1. mm(0,j)=[P[0]=T[j]], and mm(i,j)=mm(i−1,j−1)+[P[i]=T[j]] for 1≤i≤j.
Proof. The first claim is the definition with i=0. For i≥1, split the positions q≤i into q≤i−1 and q=i. The positions q≤i−1 compare P[q] with T[(j−1)−(i−1)+q], and they count mm(i−1,j−1). The position q=i compares P[i] with T[j]. □
Invariant. After the loop body for index j, for each i with 0≤i≤m−1:
- Si<2B−1, and Oi∈{0,2B−1};
- if i≤j and Oi=0, then Si=mm(i,j);
- if i≤j and Oi=2B−1, then mm(i,j)≥2B−1.
Before the loop, S=O=0, so part 1 holds, and parts 2 and 3 are empty. Call this the state after step −1.
Step. Assume the invariant after step j−1, and let S′, O′ be the vectors after step j.
No carry: in line L5, the shift moves each field i−1 to field i for 1≤i≤m−1, puts 0 in field 0, and drops field m−1 by the reduction mod 2mB. The shifted field is below 2B−1 by part 1, and M[T[j]]i≤1, so the sum of the two is at most 2B−1≤2B−1. So the addition creates no carry between fields, and after line L5 the field i is ui=Si−1+[P[i]=T[j]] for i≥1, and u0=[P[0]=T[j]]. Each ui is at most 2B−1, so its top bit is set exactly when ui=2B−1.
Line L6 gives Oi′=Oi−1∨(top bit of ui) for i≥1 and $O'_0 = $ the top bit of u0, where both sides are 0 or 2B−1 and the shift again moves whole fields. Line L7 gives Si′=ui when ui<2B−1 and Si′=0 when ui=2B−1. So part 1 holds after step j.
Fix i≤j. Case i=0: u0=mm(0,j)≤1 by Lemma 1. If O0′=0, then S0′=u0=mm(0,j). If O0′=2B−1, then u0=2B−1, which needs B=1, and then mm(0,j)=1=2B−1. Case i≥1: then i−1≤j−1, so the invariant applies to field i−1 after step j−1.
- If Oi−1=2B−1, then mm(i−1,j−1)≥2B−1 by part 3, so mm(i,j)≥2B−1 by Lemma 1. Also Oi′=2B−1. Part 3 holds, and part 2 is empty.
- If Oi−1=0, then Si−1=mm(i−1,j−1) by part 2, so ui=mm(i,j) by Lemma 1. If ui<2B−1, then Oi′=0 and Si′=ui=mm(i,j), which is part 2. If ui=2B−1, then Oi′=2B−1 and mm(i,j)=2B−1, which is part 3.
So the invariant holds after each step. □
Theorem. For m≥1, line L8 reports i=j−m+1 exactly when 0≤i≤n−m and P and T[i…i+m) differ in at most k positions.
Proof. Line L8 runs for j=m−1,…,n−1, which gives exactly the starts i=j−m+1 in 0,…,n−m. For such j, the field index m−1 is at most j, and mm(m−1,j) is the number of positions where P and T[j−m+1…j] differ. By part 1 of the invariant, Sm−1<2B−1 and Om−1 is 0 or 2B−1, so field m−1 of S∨O is Sm−1+Om−1. If Om−1=0, this value is mm(m−1,j) by part 2, and the test is the condition of the theorem. If Om−1=2B−1, the value is at least 2B−1≥k+1, so the test fails, and mm(m−1,j)≥2B−1>k by part 3, so the window is not a match either. ■