From DP Columns to Bit Vectors
For pattern $P$ of length $m$ and text prefix $T[0\dots j)$, let $D[i,j]$ be the Levenshtein distance between $P[0\dots i)$ and $T[0\dots j)$. Adjacent vertical values differ by only $-1$, $0$, or $+1$. Myers’ algorithm stores these differences instead of all $m+1$ cells: bit $i$ of $VP$ marks a $+1$ step, bit $i$ of $VN$ marks a $-1$ step, and an unset bit in both represents zero. A scalar score retains the bottom cell $D[m,j]$.
Initially $D[i,0]=i$, so $VP=2^m-1$, $VN=0$, and the score is $m$. For approximate substring matching, set the top-row boundary to $D[0,j]=0$: this is implemented by shifting a zero, rather than a one, into $P_H$. After consuming $T[j]$, the score is the minimum distance between $P$ and a substring ending at $j$. Thus one machine-word update advances an entire DP frontier.
Pattern Equality Masks
Preprocessing constructs one equality mask $Eq[c]$ per pattern symbol. Bit $i$ is set precisely when $P[i]=c$:
$$ Eq[c]=\bigvee_{\{i:P[i]=c\}}2^i. $$
For $P=\texttt{GAT}$, $Eq[G]=001_2$, $Eq[A]=010_2$, and $Eq[T]=100_2$. One lookup therefore identifies every pattern position matching the next text symbol. The alphabet changes only how masks are stored. Dense arrays suit small encoded alphabets, while dictionaries store only symbols present in a large or sparse pattern.
Unicode requires an explicit symbol contract. Code-point matching works naturally with dictionary keys, but canonically equivalent spellings compare differently unless normalized. If the application means user-perceived characters, segment both inputs into grapheme clusters using the same Unicode version before constructing masks.
| Alphabet model | Mask representation | Preprocessing | Lookup | Memory | Important constraint |
|---|---|---|---|---|---|
| DNA or tiny fixed alphabet | Dense array indexed by encoded symbol | $O(m+\lvert\Sigma\rvert)$ | $O(1)$ worst-case | $O(\lvert\Sigma\rvert)$ words | Define behavior for ambiguous symbols such as N |
| Bytes | Array of 256 masks | $O(m+256)$ | $O(1)$ worst-case | 256 words | Operates on encoded bytes, not Unicode characters |
| ASCII | Array of 128 masks | $O(m+128)$ | $O(1)$ worst-case | 128 words | Reject or separately handle non-ASCII input |
| Large sparse alphabet | Hash map from symbol to mask | $O(m)$ expected | $O(1)$ expected | $O(\min(m,\lvert\Sigma\rvert))$ words | Missing keys must return the zero mask |
| Ordered static alphabet | Sorted (symbol, mask) pairs | $O(m+\sigma_P\log\sigma_P)$ | $O(\log\sigma_P)$ | $O(\sigma_P)$ words | Deterministic lookup; $\sigma_P$ counts pattern symbols |
| Unicode code points | Hash map keyed by code point | $O(m)$ expected | $O(1)$ expected | $O(\sigma_P)$ words | Normalize if canonical equivalence matters |
| Unicode grapheme clusters | Segment, then hash cluster strings | $O(m)$ after segmentation | $O(1)$ expected | $O(\sigma_P)$ variable-size keys | Fix normalization and segmentation semantics |
The Update Equations
Let $VP$ and $VN$ encode the current column, and let $Eq$ be the equality mask for the next text symbol. For an $m$-bit mask, compute
$$ \begin{aligned} X_V &= Eq\lor VN,\\ X_H &= (((Eq\land VP)+VP)\oplus VP)\lor Eq,\\ P_H &= VN\lor\neg(X_H\lor VP),\\ M_H &= VP\land X_H. \end{aligned} $$
Before shifting, the highest pattern bit changes the bottom score: increment when it is set in $P_H$, and decrement when it is set in $M_H$. Then update
$$ \begin{aligned} P_H &\leftarrow(P_H\ll1)\lor b, & M_H&\leftarrow M_H\ll1,\\ VP'&=M_H\lor\neg(X_V\lor P_H), & VN'&=P_H\land X_V. \end{aligned} $$
Here $b=1$ gives global prefix alignment, while $b=0$ gives the free-start boundary used for approximate substring search. Complements and shifts must be restricted to the lowest $m$ bits when integers are unbounded or unused high bits could contaminate later operations.
One Myers Step
Use pattern GAT, assigning bit 0 to G, bit 1 to A, and bit 2 to T. Initially $VP=111_2$, $VN=000_2$, and the score is 3. Process G, whose equality mask is $Eq=001_2$.
- $X_V=001\lor000=001$.
- $X_H=(((001\land111)+111)\oplus111)\lor001=111$ after retaining three bits.
- $P_H=000\lor\neg(111\lor111)=000$ within three bits.
- $M_H=111\land111=111$.
- The high bit
100is set in $M_H$, so the score falls from 3 to 2. - For free-start search, shift in zero: $P_H=000\ll1=000$ and $M_H=(111\ll1)\land111=110$.
- $VP'=110\lor\neg(001\lor000)=110$ and $VN'=000\land001=000$.
The encoded differences are $(0,+1,+1)$, reconstructing the free-start column $[0,0,1,2]$. Its bottom value 2 is the best distance between GAT and a substring ending at this G.
Implementation and Extensions
When $m\le w$, each text symbol costs a constant number of $w$-bit operations, giving $O(m+n)$ preprocessing and scanning time. Native implementations should use unsigned words and mask complements. When $m>w$, store each vector in $\lceil m/w\rceil$ blocks and propagate addition and shift carries between blocks, yielding $O(n\lceil m/w\rceil)$ time.
Python integers already provide multiword arithmetic, so the same recurrence works for long patterns, although its cost grows with the number of internal limbs. The bounded mode below rejects patterns exceeding a specified word width; the unbounded mode is a compact multiword reference. Search results contain inclusive endpoints and scores. Recovering starts or edit scripts requires verification or additional traceback state.