Bitmask as a Set
When the universe of values is small - 26 letters, n <= 32, a fixed alphabet - a single integer is a set. Bit x is set exactly when element x is present.
| Operation | Expression | Cost |
|---|---|---|
add x | mask |= 1 << x | O(1) |
remove x | mask &= ~(1 << x) | O(1) |
test x | mask & (1 << x) | O(1) |
toggle x | mask ^= 1 << x | O(1) |
| union / intersection | a | b / a & b | O(1) |
| size | mask.bit_count() | O(1) |
| is complete | mask == (1 << n) - 1 | O(1) |
Every one of those is a single CPU instruction, with no hashing, no allocation, and no pointer chasing. A set does the same job in the same asymptotic time but hundreds of times slower in constant factor, and it cannot be compared, unioned, or used as a dictionary key for free.
The tell that a problem wants a bitmask: "contains only characters from", "collect all of 1..k", "which elements appear in both", or any subset-of-a-small-universe question.
Sets as Bitmasks
Build one mask per group, then answer the question with a single operator. Containment is word_mask & ~allowed == 0. Membership in both arrays is a & b. Completeness is a comparison against the all-ones mask. Duplicate detection is a test-then-set.
1684. Count the Number of Consistent Strings
Encode the allowed characters as a 26-bit mask - bit ord(c) - ord('a') is set for each allowed letter. A word is consistent only if every one of its characters is in that mask.
Start assuming all words are consistent (count = len(words)). For each word, the moment a character's bit is not in the mask (pos & mask == 0), the word is inconsistent: decrement and break.
- Time
- O(M)
- M is the total number of characters across all words.
- Space
- O(1)
- A single integer mask of fixed width.
2032. Two Out of Three
2032Two Out of Three
Build one bitmask per array, setting bit v for every value v present. Since values are in [1, 100], a single integer per array is enough.
Then for each candidate value i, count in how many of the three masks bit i is set. If at least two of them contain it, i qualifies for the answer.
- Time
- O(N + V)
Nis the total elements across the three arrays, scanned once each to build the masks;Vis the value range scanned afterward (here 100) to test each candidate.- Space
- O(V)
bm1,bm2, andbm3each needVbits to represent values up to 100, andanscan hold up toVqualifying values.
2351. First Letter to Appear Twice
Track which letters have been seen using a 26-bit mask, where bit ord(c) - ord('a') marks a letter. Walk the string left to right.
For each character, if its bit is already set in bitmap (pos & bitmap), this is the first repeat - return it immediately. Otherwise set the bit and continue.
- Time
- O(n)
n = len(s). Thefor i in sloop visits each character once, stopping as soon aspos & bitmapfinds a repeat.- Space
- O(1)
- Only the single fixed-width
bitmapinteger is stored; nothing scales withn.
2869. Minimum Operations to Collect Elements
Each operation removes the last element of nums, so collecting 1..k means consuming a suffix. We want the shortest suffix that contains all of 1..k.
Build a target mask with bits 1..k set: (1 << k) - 1 gives bits 0..k-1, then a left shift by one aligns them to positions 1..k. Scan from the back, ORing each value's bit into bitmap. The first time bitmap & mask == mask, every required element has appeared - return how many elements were consumed.
- Time
- O(n)
- The
for i in range(len(nums) - 1, -1, -1)loop walks backward throughnumsat most once, doingO(1)bitmask work per element, so total isO(n), wheren = len(nums). - Space
- O(1)
- Two fixed-width integer masks.
2917. Find the K-or of an Array
The K-or sets bit pos in the result if at least k of the array elements have that bit set. So handle each bit position independently.
For each position pos (0 to 31), count how many numbers have n & (1 << pos) set. If that tally reaches k, OR bit pos into the answer mask.
- Time
- O(32 N)
- For each of 32 bit positions, scan all N numbers.
- Space
- O(1)
- A single accumulating mask.
3173. Bitwise OR of Adjacent Elements
The result has one fewer element than the input: index i is nums[i] | nums[i + 1]. itertools.pairwise walks consecutive pairs directly, so the whole answer is a single comprehension over those pairs.
- Time
- O(n)
- One pass over the
n - 1adjacent pairs. - Space
- O(n)
- The output list holds
n - 1values.
2657. Find the Prefix Common Array of Two Arrays
Track which values have appeared so far in each array as a bitmask: set bit A[i] in bitmap_a and bit B[i] in bitmap_b. The values present in both prefixes are exactly the bits set in bitmap_a & bitmap_b, so counting those set bits gives each prefix's common count in one shot.
- Time
- O(N²)
- The outer loop runs
Ntimes (N = len(A)), each iteration doingO(1)bit-shift ORs to updatebitmap_a/bitmap_b. hammingWeightunsets one bit per iteration (n & (n - 1)), so its cost equals the number of set bits inbitmap_a & bitmap_b. Afteriinsertions that mask can have up toiset bits, so thei-th call costsO(i)in the worst case - summed over allNiterations that'sO(1 + 2 + ... + N) = O(N²).- Space
- O(N)
ansholdsNentries, andbitmap_a/bitmap_beach grow toO(N)bits wide as values up toNget OR'd in.
1371. Find the Longest Substring Containing Vowels in Even Counts
Only the parity of each vowel's count matters, so track it as a 5-bit mask - bit vowels[c] flips every time character c is seen. A substring has all-even vowel counts exactly when its prefix masks at both ends are equal, since the XOR of equal masks is zero.
hashmap remembers the first index where each mask value occurred (hashmap[0] = -1 seeds the empty prefix). Whenever the current mask repeats, everything between the two occurrences has balanced vowels, and idx - hashmap[bitmask] is a candidate length.
- Time
- O(N)
- One pass over
s, constant work per character. - Space
- O(1)
bitmaskonly has 32 possible values, sohashmapnever holds more than 32 entries.
41. First Missing Positive
The smallest missing positive integer must lie in the range [1, len(nums) + 1], so anything outside that range can be ignored. Instead of marking presence in the array itself, this solution packs presence into a single integer bitmask - bit num is set when num appears.
Steps:
- Bitmask creation - iterate through
nums, setting bitnumfor each positive integer that is at mostlen(nums). - Negation - flip all bits of the bitmask. The chosen
bit_lengthislen(nums) + 2. - Unset the 0th bit -
0is not a positive number, so clear bit0with(negation | 1) ^ 1. - Isolate the least significant set bit -
negation & -negationkeeps only the rightmost1-bit, whose position is the smallest missing positive.log2(ls_set)recovers that position.
Why + 2 in bit_length? If the input is [1], the bitmask is 0b10. To negate correctly we need 0b101 and not 0b01, which only happens when bit_length = 3, that is +2 of len([1]).
Worked examples:
[1, 2, 0]- bitmask0b110, negation0b11001, after unsetting bit 00b11000, isolated0b1000,log2 = 3.[3, 4, -1, 1]- bitmask0b11010, negation0b100101, after unsetting0b100100, isolated0b100,log2 = 2.[7, 8, 9, 11, 12]- bitmask0b0, negation0b1111111, after unsetting0b1111110, isolated0b10,log2 = 1.[1]- bitmask0b10, negation0b101, after unsetting0b100, isolated0b100,log2 = 2.
- Time
- O(N)
- A single pass builds the bitmask, and the bit isolation is constant time.
- Space
- O(1)
- Only a fixed set of integer variables is used (the bitmask width is bounded by the array length).
448. Find All Numbers Disappeared in an Array
Closely related to 41. First Missing Positive. Every value in nums lies in [1, n], so presence is recorded as a bit in a single integer; the bits that remain unset after the scan are exactly the missing numbers.
Steps:
- Bitmask creation - iterate through
nums, setting bitnumberfor each value seen. - Negation - flip all bits using
bit_length = len(nums) + 1, so set bits now mark absent values. - Unset the 0th bit -
0is outside the range, so clear it with(negation | 1) ^ 1. - Collect missing numbers - walk the negated bitmask and append every position whose bit is still set.
Worked examples:
[4, 3, 2, 7, 8, 2, 3, 1]- bitmask0b110011110, negation0b1100001, after unsetting bit 00b1100000, answer[5, 6].[1, 1]- bitmask0b10, negation0b101, after unsetting0b100, answer[2].[1, 1, 1]- bitmask0b10, negation0b1101, after unsetting0b1100, answer[2, 3].
- Time
- O(2n)
- Two separate linear passes: the
for number in numsloop buildsbitmask, and thefor pos in range(negation.bit_length())loop collects the missing numbers - both bounded byn = len(nums), giving2n. - Space
- O(1)
- Only a fixed-size bitmask and a few auxiliary variables are used (the output list is not counted).
442. Find All Duplicates in an Array
Every value lies in [1, n], so a single integer bitmask can record which values have already been seen. For each number, bit number acts as a "seen" flag - if that bit is already set, the value is a duplicate; otherwise we set it.
This avoids any extra hash set: the bitmask holds the entire seen-state in one integer.
Walkthrough on [4, 3, 2, 7, 8, 2, 3, 1]:
4, 3, 2, 7, 8are all first-time - their bits get set.- The second
2finds bit2already set, so2is recorded as a duplicate. - The second
3finds bit3already set, so3is recorded. 1is first-time. Final answer:[2, 3].
- Time
- O(n)
nis the length ofnums- thefor number in numsloop makes a single pass, with constant-time bit operations per element.- Space
- O(n)
bitmaskneeds one bit per possible value in[1, n]- since Python integers grow with their bit width, holding a value that wide costsO(n)bits, notO(1)(the output list is not counted).