Purpose: The complemented formulation of Bitap: exact string matching in using one shift and one OR per text character.
The Difference from Shift-And
Shift-and keeps a state word where bit = 1 means “prefix of length matches”. Shift-or stores the complement: bit = 0 means the prefix matches.
Precompute masks with the bits inverted: has bit equal to 0 iff pattern[j] == c.
The shift brings in a 0 in the low bit for free — exactly the “start a new match attempt” semantics — so no | 1 is needed. A match ends at iff bit of is 0.
Code
vector<int> shiftOr(const string& text, const string& pat) {
int m = pat.size();
if (m == 0 || m > 64) return {};
array<uint64_t, 256> B; B.fill(~0ULL);
for (int j = 0; j < m; j++) B[(unsigned char)pat[j]] &= ~(1ULL << j);
vector<int> hits;
uint64_t D = ~0ULL, msb = 1ULL << (m - 1);
for (int i = 0; i < (int)text.size(); i++) {
D = (D << 1) | B[(unsigned char)text[i]];
if (!(D & msb)) hits.push_back(i - m + 1);
}
return hits;
}Two machine instructions in the inner loop. On a modern CPU this saturates memory bandwidth on the text — it is hard to beat for short patterns.
Complexity
- Time: , i.e. for
- Preprocessing:
- Space:
Why It Works
The state word is a bitmask of the active states of the trivial NFA that recognises .*pattern. State is active after reading iff pattern[0..j] is a suffix of that prefix of the text. The transition “state becomes state if the character matches” is exactly a left shift followed by masking out positions where the character disagrees. Complementing turns the mask-out from an AND into an OR. ∎
Extending it for free
Because is just “which pattern positions can this character occupy”, several features cost nothing:
| Feature | How |
|---|---|
Wildcard ? | clear bit in for every |
Character class [abc] | clear bit in for a, b, c |
| Case-insensitive | clear the bit for both cases |
| Approximate ( errors) | keep state words — see Bitap |
Variants / Use Cases
- Bitap / shift-and — the same algorithm, uncomplemented, plus the approximate-matching extension
- Patterns longer than a word — chain several words with carry propagation; the constant grows but it stays linear
- KMP — the choice for long patterns where becomes large
- Aho-Corasick — for many patterns simultaneously
- Bitset optimization — the general “simulate an automaton 64 states at a time” technique