Convolution for Matching
Convolution converts alignment comparisons into coefficient arithmetic. For each symbol $c$, let $T_c[i]=1$ when text position $i$ contains $c$, and let $P_c[m-1-j]=1$ when pattern position $j$ contains $c$. Then $(T_c*P_c)[i+m-1]$ counts positions where both aligned symbols equal $c$. Summing over the alphabet gives the agreement count; without wildcards, the Hamming distance is $m$ minus that count.
For wildcard matching, omit wildcard positions from every pattern indicator. If the pattern has $k$ concrete positions, an alignment is valid exactly when its agreement count is $k$. One transform per distinct pattern symbol costs $O(\sigma N\log N)$ for combined length $N$. Algebraic encodings can reduce the number of transforms, while an NTT gives exact modular coefficients when its modulus supports the transform length.
Floating-point FFT coefficients require rounding and can fail when accumulated error approaches half an integer. An NTT avoids rounding but is exact only modulo its prime: verify transform-length support and bound true coefficients below the modulus, or reconstruct from enough independent primes to prevent wraparound.
Randomized Fingerprints and Streaming Equality
A Karp–Rabin fingerprint treats a string as a polynomial: $H(S)=\sum_{i=0}^{m-1}s_i b^{m-1-i}\pmod p$. Prefix hashes and powers extract equal-length substring hashes in $O(1)$ after linear preprocessing, while a rolling window removes the outgoing contribution, multiplies by $b$, and adds the incoming symbol.
Equal strings always hash equally; unequal strings may collide. If the compared length is at most $L$ and $b$ is random in $\mathbb F_p$, their nonzero difference polynomial has at most $L-1$ roots, so one comparison collides with probability at most $(L-1)/p$. Across $Q$ comparisons, the union bound is $Q(L-1)/p$. Independent moduli or bases multiply the per-comparison probabilities.
Hash-only equality is Monte Carlo. A Las Vegas design verifies candidates against the original symbols and is always correct, with randomness affecting runtime only. Streaming hashes retain only a polynomial state; linear sketches extend this idea to mismatch estimation, reconciliation, and compact communication.
Budget the Failure Probability
Suppose $Q=10^6$ substring queries compare strings of length at most $L=10^5$ using one fingerprint modulo $p=2^{61}-1$.
For one query, $\Pr[\text{collision}]\le(L-1)/p\lt10^5/(2^{61}-1)\approx4.34\times10^{-14}$. Across the full workload, the union bound gives $Q(L-1)/p\lt10^{11}/(2^{61}-1)\approx4.34\times10^{-8}$, about one failure budget per $23$ million workloads.
With two independent $61$-bit fingerprints, the workload bound is approximately $10^6(4.34\times10^{-14})^2\approx1.88\times10^{-21}$. If even a probabilistic bound is unacceptable, compare the underlying substrings whenever both fingerprints match.
Dynamic and Persistent Strings
Static prefix hashes break under insertion or deletion because later indices shift. A dynamic string instead uses an implicit balanced tree or rope. Each node stores subtree length and a composable hash, with concatenation $H(XY)=H(X)b^{|Y|}+H(Y)\pmod p$. Splits and joins support insertion, deletion, concatenation, and substring isolation in logarithmic time.
Point replacement changes one leaf and recomputes aggregates along a root path. Maintaining forward and reverse hashes supports palindrome queries and lazy reversals. For fixed-length arrays with point updates, Fenwick and segment trees can maintain weighted character contributions, though insertions favor implicit trees.
Ropes store chunks at leaves for cache locality. Persistent path copying creates a new root per edit while sharing untouched subtrees, enabling historical queries, undo, and branches with $O(\log n)$ new nodes per update. Hashes remain probabilistic, so store lengths, use independent moduli when justified, and verify security-sensitive equality.
flowchart TD
EDIT["Edit position i"]
DESCEND["Descend using subtree lengths"]
LEAF["Replace or split leaf chunk"]
REHASH["Recompute length and forward/reverse hashes"]
SHARE["Share untouched persistent subtrees"]
ROOT["New root represents edited string"]
EDIT --> DESCEND
DESCEND --> LEAF
LEAF --> REHASH
REHASH -->|"update ancestors"| ROOT
REHASH -->|"path copying"| SHARE
SHARE --> ROOT
Multidimensional and Structured Sequences
For grids, a two-dimensional polynomial hash uses independent row and column weights; a 2D prefix table extracts rectangular hashes in constant time. Separable matching processes rows first and then matches the sequence of row results vertically, while 2D convolution counts aligned cell agreements.
A circular string is represented by $S+S$, restricting valid rotation starts to $[0,n)$. Ordered trees can be serialized with explicit boundaries; unordered trees need canonical child ordering or multiset hashing. Token streams should be normalized to stable integer IDs before applying hashes, suffix structures, LCS, or edit distance.
Numerical sequences may require adjacent differences, ranks, quantization, or tolerance-aware comparison to capture the intended invariance. DNA supports two-bit packing for A, C, G, and T, but ambiguous IUPAC symbols need masks, and reverse-complement search must apply biological complement rules.
| Sequence domain | Useful representation | Suitable algorithms | Required adaptation | Common pitfall |
|---|---|---|---|---|
| Character grid or image | 2D prefix hashes and per-symbol planes | 2D Rabin–Karp, separable matching, convolution | Combine row and column coordinates | Trusting hash equality without verification |
| Circular strings | $S+S$ with starts restricted to $[0,n)$ | KMP, Z, Booth, rolling hashes | Prevent unintended matches beyond one cycle | Returning duplicated start $n$ |
| Ordered rooted trees | Delimited traversal or subtree hashes | Tree isomorphism and subtree matching | Encode child order and boundaries | Ambiguous serialization |
| Unordered rooted trees | Sorted child signatures | Canonical forms and multiset hashes | Remove sibling-order dependence | Sorting labels instead of subtree forms |
| Source code or documents | Lexer tokens mapped to integer IDs | Suffix arrays, LCS, token edit distance | Decide normalization of trivia and names | Confusing lexical and semantic equivalence |
| Numerical time series | Quantized values, ranks, or differences | Convolution, correlation, DTW | Define tolerance and invariance | Exact floating-point equality |
| DNA and RNA | Two-bit bases plus ambiguity masks | Bit-parallel matching, FM-indexes, alignment | Handle IUPAC symbols and reverse complements | Inconsistent treatment of N |
| Protein sequences | Integer-coded amino acids | Alignment, substitution matrices, seed-and-extend | Use biological scores such as BLOSUM | Applying nucleotide assumptions |
External-Memory and Hardware-Aware Processing
A streaming matcher must preserve state across chunks. KMP can carry its automaton state directly. A stateless matcher can prepend the previous chunk's last $m-1$ symbols to the next chunk so every length-$m$ boundary-spanning occurrence is visible, then deduplicate starts assigned to the overlap.
Packed alphabets reduce memory traffic: DNA fits in two bits, byte classes fit masks, and bitsets process $w$ positions per word. Shift-Or, Myers edit matching, bitset LCS, and population-count Hamming distance turn many scalar operations into word operations. SIMD adds lane parallelism but must handle unaligned prefixes, tails, and cross-vector matches.
Parallel text blocks require sufficient overlap or explicit transfer of automaton boundary states. For external memory, sequential scans, blocked indexes, buffering, compressed postings, and cache-aware layouts minimize I/O. Chunk size balances overlap overhead, decompression granularity, parallelism, cache capacity, and storage latency.
Next in the Series
In Engineering, Selection & Production Correctness, we'll explore Testing, adversarial safety, performance engineering, Unicode contracts, and workload-driven selection.