Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
What is Bit Manipulation?
Bit Manipulation uses bitwise operators to solve problems at the binary level. It enables O(1) operations for many tasks and reduces space complexity. Think of every integer as a row of light switches — each switch is either ON (1) or OFF (0). Bit manipulation is the art of flipping, reading, and combining those switches directly, without converting to decimal. This is the language your CPU already speaks, which is why bitwise operations are as fast as operations can get. Why does this matter for interviews? Bit manipulation lets you solve certain problems in O(1) space and O(n) time that would otherwise require hash sets or sorting. The XOR trick for “find the unique element” is a classic example: no extra memory, one pass, elegant. Interviewers love it because it tests whether you understand what is happening beneath the abstraction. Key mental model: Every integer is a fixed-width binary string (32 or 64 bits in most languages). Operations like AND, OR, and XOR operate on each bit position independently and in parallel — all 32 bits are processed in a single CPU instruction.Essential Operations
Common Tricks
Each trick exploits a specific property of binary representation. Understanding why each works is more important than memorizing the formulas.Pattern Variations
1. Single Number
The poster child of bit manipulation. XOR has three properties that make this work:- Self-inverse:
a ^ a = 0(any number XOR itself vanishes) - Identity:
a ^ 0 = a(XOR with zero changes nothing) - Commutative and associative: order does not matter
2. Counting Bits
A beautiful DP-meets-bit-manipulation problem. The recurrenceresult[i] = result[i >> 1] + (i & 1) says: “the number of set bits in i equals the number of set bits in i with its last bit removed (i >> 1), plus whatever that last bit was (i & 1).”
Walked example: i=5 (binary 101). i >> 1 = 2 (binary 10, has 1 set bit). i & 1 = 1. So result[5] = 1 + 1 = 2. Correct: 101 has two 1-bits.
Time: O(n). Space: O(n).
3. Subsets Using Bitmask
An elegant alternative to backtracking for generating subsets. Each integer from 0 to 2^n - 1 is a bitmask where biti means “include nums[i].” The bitmask 5 (binary 101) represents the subset containing nums[0] and nums[2].
This approach is often simpler to code and harder to get wrong than recursive backtracking, but it only works when n is small (n at most around 20, since 2^20 is about a million).
Time: O(n * 2^n). Space: O(1) (excluding output).
4. Missing Number
5. Reverse Bits
Classic Problems
1. Power of Two
1. Power of Two
2. Hamming Distance
2. Hamming Distance
3. Add Without Plus
3. Add Without Plus
4. Maximum XOR
4. Maximum XOR
Bit Manipulation in DP
Common Mistakes
Quick Reference
| Operation | Code | Description |
|---|---|---|
| Get bit | (n >> i) & 1 | Get ith bit |
| Set bit | n OR (1 left-shift i) | Set ith bit to 1 |
| Clear bit | n & ~(1 left-shift i) | Set ith bit to 0 |
| Toggle bit | n ^ (1 left-shift i) | Flip ith bit |
| Lowest bit | n & (-n) | Isolate lowest set bit |
| Clear lowest | n & (n-1) | Remove lowest set bit |
Operator and Trick Cheatsheet
A condensed reference you can re-derive in an interview. Memorize the why, not just the line.| Goal | One-liner | Why it works |
|---|---|---|
| AND | a & b | Both bits set |
| OR | a | b | Either bit set |
| XOR | a ^ b | Bits differ |
| NOT | ~a | Flip every bit (in fixed width) |
| Get bit i | (n >> i) & 1 | Move bit i to position 0, mask |
| Set bit i | n | (1 << i) | Mask in a 1 at position i |
| Clear bit i | n & ~(1 << i) | Mask out position i |
| Toggle bit i | n ^ (1 << i) | XOR flips that bit |
| Lowest set bit | n & (-n) | Two’s complement isolates it |
| Clear lowest set bit | n & (n - 1) | Brian Kernighan |
| Is power of 2? | n > 0 && (n & (n - 1)) == 0 | Exactly one set bit |
| Flip all bits in fixed width | n ^ -1 (or n ^ ((1 << w) - 1)) | XOR with all-ones mask |
| Multiply by 2 | n << 1 | Left shift |
| Divide by 2 (unsigned) | n >> 1 | Logical right shift |
| Swap without temp | a ^= b; b ^= a; a ^= b; | XOR cancels pairs |
Iterating Subsets of a Bitmask
A surgical trick that shows up in bitmask DP. Given a bitmaskmask, enumerate every subset (including empty) in time proportional to the number of subsets, not the universe.
mask strips the new lower bits that are not part of the original set. Each step lands on a strict subset of mask, eventually reaching 0. Total iterations: exactly 2^popcount(mask).
Where it shows up: sum-over-subsets DP, partition problems (LC 1125 Smallest Sufficient Team), travelling-salesman-style bitmask DP, “split set into two non-empty groups” interval problems.
Worked LeetCode Problems
Six canonical bit problems mapped to the patterns above. Each one is a template you can copy-adapt in interviews.LC 136 — Single Number (Easy)
Pattern: XOR cancellation. Every element appears twice except one.(a ^ a) ^ (b ^ b) ^ ... ^ unique = unique. O(n) time, O(1) space.
LC 191 — Number of 1 Bits (Easy)
Pattern: Brian Kernighan’s algorithm.for i in range(32): count += (n >> i) & 1.
LC 338 — Counting Bits (Easy, DP + bit insight)
Pattern: DP recurrence using the lowest-bit trick.dp[i] = dp[i >> 1] + (i & 1)— chop off the lowest bit, look up half.dp[i] = dp[i & (i - 1)] + 1— clear the lowest set bit, add one.
LC 268 — Missing Number (Easy)
Pattern: XOR with the full range. Cancels every present number.0 ^ 1 ^ ... ^ n XOR’d against nums (which contains all but one of those values) leaves only the missing one. The len(nums) seed accounts for the upper bound n not being one of the loop indices.
LC 78 — Subsets (Medium, bitmask iteration)
Pattern: Each integer in[0, 2^n) is a subset bitmask.
i decides whether nums[i] is included. Generates all 2^n subsets in O(n * 2^n) total — same asymptotic cost as backtracking but with no recursion stack.
LC 421 — Maximum XOR of Two Numbers in an Array (Medium, trie + bit greedy)
Pattern: Build a binary trie of bits MSB-to-LSB. For each number, walk the trie greedily preferring the opposite bit to maximize XOR.Curated LeetCode List
A focused list grouped by sub-pattern. Solve in order; you will see the same five tricks reused.| LC # | Problem | Difficulty | Sub-pattern |
|---|---|---|---|
| 136 | Single Number | Easy | XOR cancellation |
| 191 | Number of 1 Bits | Easy | Brian Kernighan |
| 231 | Power of Two | Easy | n & (n - 1) == 0 |
| 268 | Missing Number | Easy | XOR with range |
| 338 | Counting Bits | Easy | DP plus bit recurrence |
| 78 | Subsets | Medium | Bitmask enumeration |
| 137 | Single Number II | Medium | Bit-counting mod 3 |
| 260 | Single Number III | Medium | XOR plus partitioning bit |
| 371 | Sum of Two Integers | Medium | XOR plus carry loop |
| 421 | Maximum XOR of Two Numbers | Medium | Trie or prefix greedy |
| 477 | Total Hamming Distance | Medium | Per-bit counting |
| 1125 | Smallest Sufficient Team | Hard | Bitmask DP |
Practice Problems
| Problem | Difficulty | Link |
|---|---|---|
| Single Number | Easy | LeetCode 136 |
| Number of 1 Bits | Easy | LeetCode 191 |
| Counting Bits | Easy | LeetCode 338 |
| Maximum XOR | Medium | LeetCode 421 |
Interview Questions
1. An array contains n numbers where every element appears twice except one. Find the unique element in O(n) time and O(1) space.
What interviewers are really testing: Whether XOR is your first instinct and whether you can explain the three properties (self-inverse, identity, associativity) that make it work. This is the gateway bit manipulation question. Strong Answer:- XOR all elements together. Every duplicate pair cancels out (
a ^ a = 0), and the unique element survives (a ^ 0 = a). One pass, no extra memory. - The three properties that make this work: self-inverse (
a ^ a = 0), identity (a ^ 0 = a), and commutativity/associativity (order does not matter, so we can mentally regroup pairs). - Walked example:
[4, 1, 2, 1, 2]— XOR all:4 ^ 1 ^ 2 ^ 1 ^ 2 = 4 ^ (1 ^ 1) ^ (2 ^ 2) = 4 ^ 0 ^ 0 = 4. - Why not a hash set? A hash set works but uses O(n) space. The XOR approach is O(1) space, which is what makes it elegant. Interviewers specifically want to see that you reach for bitwise operations when the constraints demand zero extra space.
- What if every element appears three times except one? Does XOR still work? What technique handles this?
- What if there are two unique elements (everything else appears twice)? How do you separate them using XOR?
2. How does n & (n - 1) work, and what problems does it solve?
What interviewers are really testing: Whether you understand the binary-level mechanics of this operation, not just the applications. This one expression solves power-of-two checks, Brian Kernighan’s popcount, and more.
Strong Answer:
n - 1flips the lowest set bit ofnand sets all bits below it. Son & (n - 1)turns off the lowest set bit and leaves everything else unchanged. Example:n = 12(binary1100),n - 1 = 11(binary1011),n & (n-1) = 8(binary1000) — the lowest set bit (position 2) got cleared.- Power-of-two check: a power of 2 has exactly one bit set.
n & (n - 1)clears that single bit, giving 0. Son > 0 and (n & (n - 1)) == 0is the O(1) power-of-two test. Then > 0guard is essential because0 & (-1) == 0is also true, but 0 is not a power of 2. - Brian Kernighan’s algorithm: to count set bits (popcount), repeatedly apply
n = n & (n - 1)untilnbecomes 0, counting iterations. Each iteration removes exactly one set bit, so the loop runs exactlypopcount(n)times — much faster than checking all 32 bits when the number has few set bits. - This trick also detects whether a number is a power of 4 (must be power of 2 AND have its single set bit in an even position:
n & 0x55555555 == n).
n & (n-1) checks for power of two.” Without being able to explain the bit-level mechanics, the candidate cannot adapt the trick to novel problems.
Follow-ups:
- How would you count the number of set bits in all integers from 0 to n using the DP recurrence
result[i] = result[i & (i-1)] + 1? - What is the Hamming distance between two integers, and how does XOR combined with popcount compute it?
3. Generate all subsets of an array using bitmasks. When is this better than backtracking?
What interviewers are really testing: Whether you can map the concept of subset inclusion/exclusion to binary representation and understand the practical trade-offs versus recursive backtracking. Strong Answer:- Each integer from 0 to
2^n - 1represents a subset. Bitibeing set means “includenums[i].” Iterate through all2^nmasks, and for each mask, iterate through n bits to build the subset. Total time: O(n * 2^n). - Example: for
nums = [a, b, c], mask5(binary101) represents[a, c]. Mask0is the empty set, mask7(111) is the full set. - When it is better than backtracking: the bitmask approach is iterative, so there is no recursion overhead and no risk of stack overflow. It is also easier to reason about correctness — you are guaranteed to enumerate exactly
2^nsubsets without duplicates. For n at most 20 (about a million subsets), it is practical and often simpler to code under interview pressure. - When backtracking is better: for large n where you need to prune early (like “subsets with sum at most a target”), backtracking can skip entire branches. Bitmask enumeration always visits all
2^nmasks — no pruning possible. Backtracking also generalizes to permutations and combinations, while bitmask is specific to subset enumeration.
- How would you enumerate all subsets of size exactly k using bitmasks? What is the trick to go from one k-bit mask to the next lexicographic k-bit mask?
- In competitive programming, people use bitmask DP (like Travelling Salesman). How does the bitmask represent the DP state?
4. Explain the difference between signed and unsigned right shift. Why does this matter in bit manipulation interviews?
What interviewers are really testing: Language-level understanding of how negative numbers interact with bitwise operations. This is a frequent source of bugs in Java and C++, and interviewers want to see awareness. Strong Answer:- Arithmetic right shift (
>>) preserves the sign bit — it fills new bits on the left with the sign bit (0 for positive, 1 for negative). Logical/unsigned right shift (>>>in Java) always fills with 0, regardless of the sign. - In Java, this distinction is critical.
(-1) >> 1is still-1(binary all-1s stays all-1s).(-1) >>> 1isInteger.MAX_VALUE(the sign bit becomes 0). If you are manipulating bits of a negative number and expecting zeros to appear, you must use>>>. - In Python, integers have arbitrary precision, so there is no fixed bit width and
>>always does arithmetic shift. Negative numbers in Python are conceptually infinite-width two’s complement, which means~0is-1and bitwise operations on negatives need masking with& 0xFFFFFFFFto simulate 32-bit behavior. - In C/C++, right-shifting a negative number is implementation-defined (the standard does not mandate arithmetic vs. logical). Most compilers do arithmetic shift, but relying on this is technically non-portable. Use unsigned types when doing bit manipulation to avoid surprises.
>> and >>> or that C++ right-shift on negatives is implementation-defined.
Follow-ups:
- What is two’s complement and why do computers use it instead of sign-magnitude?
- What does
Integer.MIN_VALUE >> 1evaluate to in Java? What aboutInteger.MIN_VALUE >>> 1?
5. Find the missing number in an array containing n distinct numbers from the range [0, n]. Solve it using bit manipulation.
What interviewers are really testing: Whether you can apply the XOR cancellation trick to a slightly different setup. The missing number problem has multiple solutions (math, XOR, sorting), and the XOR approach shows bit manipulation fluency. Strong Answer:- XOR all numbers from 0 to n, then XOR all elements in the array. Every number that appears in both cancels out, leaving only the missing number.
- Implementation: initialize
result = n, then for each indexi, XOR bothiandnums[i]into result. After the loop, result is the missing number. - Why this works: the indices
0, 1, ..., n-1combined with the initialngive us exactly the complete range[0, n]. XOR-ing these with the array elements cancels all present numbers, leaving only the absent one. - Alternative approach: use the sum formula
n*(n+1)/2 - sum(nums). This is simpler but can overflow for large n in languages with fixed-width integers. The XOR approach never overflows because XOR does not increase magnitude.
- What if there are two missing numbers from the range [0, n+1]? Can you still use XOR?
- The sum formula
n*(n+1)/2can overflow in 32-bit integers. At what value of n does this happen?
6. How do you add two integers without using the + or - operator? Walk through the bit-level logic.
What interviewers are really testing: Deep understanding of how binary addition works at the hardware level. This problem maps directly to how a CPU’s adder circuit operates. Strong Answer:- Binary addition without a carry is just XOR:
1 + 1 = 0(with carry),1 + 0 = 1,0 + 0 = 0. The carry for each bit position is AND: a carry is generated when both bits are 1. - The algorithm:
sum = a ^ b(add without carry),carry = (a & b) << 1(carry bits, shifted left because a carry at position k affects position k+1). Now addsumandcarry— but we cannot use+, so we repeat the process witha = sum, b = carryuntil there is no carry (b == 0). - In Python, negative numbers require special handling because Python integers have unlimited width. You need to mask with
0xFFFFFFFFto simulate 32-bit arithmetic and convert back from unsigned to signed at the end. - This is literally how a ripple-carry adder works in hardware. XOR gates compute the sum bits, AND gates compute the carry bits, and the carry propagates through the chain. A carry-lookahead adder optimizes this by computing all carries in parallel.
a - (-b) to avoid the plus sign.” This technically uses subtraction, which is not the spirit of the question. The interviewer wants the XOR/AND loop.
Follow-ups:
- How do you handle subtraction without the minus operator?
- What is the maximum number of iterations the loop can take for 32-bit integers? Why?
7. What are common bit manipulation pitfalls in interviews, and how do you avoid them?
What interviewers are really testing: Practical awareness and defensive coding habits. This is a meta-question that reveals whether the candidate has been burned by these issues before. Strong Answer:- Operator precedence: bitwise operators have lower precedence than comparison operators in Java, C++, and Python.
x & 1 == 0is parsed asx & (1 == 0)=x & 0, not(x & 1) == 0. Always parenthesize:(x & 1) == 0. This bug is almost invisible during interviews. - Integer overflow on shift:
1 << 31in Java isInteger.MIN_VALUE(negative) because Java uses signed 32-bit ints. For 64-bit shifts, use1L << n. In C++,1 << 32on a 32-bit int is undefined behavior. - The
n = 0edge case:0 & (0 - 1)equals 0, which passes the power-of-two check, but 0 is not a power of 2. Always guard withn > 0. - Python’s unlimited precision: unlike Java/C++, Python integers grow arbitrarily.
~0is-1(not a 32-bit complement). To simulate 32-bit behavior, mask with& 0xFFFFFFFF. This matters for reverse-bits and add-without-plus problems. - Signed vs. unsigned confusion: In Java, there is no unsigned int type. Use
>>>for unsigned right shift andInteger.toUnsignedLong()when needed. In C++, mixing signed and unsigned in comparisons triggers compiler warnings for good reason — the implicit conversion can produce surprising results.
- Write a function to reverse the bits of a 32-bit integer. What is the common off-by-one error people make?
- How does the
bitsetdata structure in C++ (orBitSetin Java) differ from manual bit manipulation? When would you use one over the other?
Interview Deep-Dive
When would you choose bit manipulation over a HashMap or sorting-based approach? What are the precise conditions where bit tricks provide an advantage?
When would you choose bit manipulation over a HashMap or sorting-based approach? What are the precise conditions where bit tricks provide an advantage?
- Bit manipulation wins when the problem involves finding unique elements among duplicates and you need O(1) space. The XOR trick for “find the single number” uses zero extra memory — a HashMap uses O(n) space for the same result.
- Condition 1 — Duplicate cancellation. When elements appear in pairs (or triples with bit-counting), XOR or bitwise counting can cancel them without storing anything. This is impossible with sorting (O(n log n) time) or hashing (O(n) space).
- Condition 2 — Set representation. When you need to track subsets of a small universe (n less than 32 or 64), bitmasks replace HashSets. Each subset is a single integer, set operations become bitwise OR/AND/XOR, and iteration over subsets uses
n & (n-1)tricks. Bitmask DP for TSP (Travelling Salesman) uses this: state is a bitmask of visited cities, which would be an exponentially large set otherwise. - Condition 3 — Arithmetic properties. Problems involving powers of 2, parity, or binary representations are naturally bit-manipulation problems. Checking power of 2 is
n & (n-1) == 0— O(1) versus converting to binary string and counting. - When NOT to use bit manipulation: When the problem has no inherent binary structure, bit tricks become obfuscation rather than optimization. If a HashMap solution is clear and within time/space constraints, prefer readability.
- Practical limit: Bitmask approaches work for sets of size up to ~20-25 (since 2^25 = 33 million states). Beyond that, the exponential state space makes bitmask DP infeasible.
- You need bitwise counting modulo 3. For each bit position (0 through 31), count how many numbers have that bit set. If the count modulo 3 is 1, the unique number has that bit set; if 0, it does not.
- Implementation: use two variables
onesandtwosthat track bits appearing once and twice respectively. The state machine transitions:ones = (ones ^ num) & ~twos,twos = (twos ^ num) & ~ones. After processing all numbers,onesholds the unique number. - Time: O(n). Space: O(1). The modular counting generalizes: for “every element appears k times except one,” count each bit position modulo k.
Explain the bitmask subset enumeration technique. Given a bitmask representing a set, how do you iterate over all subsets of that set in O(2^popcount) time?
Explain the bitmask subset enumeration technique. Given a bitmask representing a set, how do you iterate over all subsets of that set in O(2^popcount) time?
- The trick: To enumerate all subsets of a bitmask
mask, start frommaskand repeatedly subtract 1 while masking with the original:sub = (sub - 1) & mask. This generates all subsets in decreasing order, terminating at 0. - Why it works: Subtracting 1 from a bitmask flips the lowest set bit and sets all lower bits. ANDing with
maskensures only bits from the original set survive. This systematically generates every combination of bits that are subsets ofmask. - Code:
- Complexity: Exactly 2^popcount(mask) subsets are generated, each in O(1) time. If mask has k bits set, this is O(2^k) total. For k = 15, that is 32,768 subsets.
- Where this appears: Bitmask DP problems where the transition requires iterating over all subsets of a state. For example, in the “Minimum Cost to Connect Clusters” problem, for each set S of nodes, you try all ways to partition S into two non-empty subsets S1 and S2 where S1 union S2 = S.
- Common mistake: Forgetting to handle the empty subset (sub = 0). The loop
while sub > 0excludes it. If you need the empty set, process it after the loop.
- There are 2^n possible masks and n possible current cities, giving 2^n * n states. Each transition tries n-1 possible next cities. Total: O(n^2 * 2^n).
- For n = 20: 20^2 * 2^20 = 400 * 1,048,576 = ~419 million operations. Feasible in 1-2 seconds in C++, borderline in Python.
- For n = 25: 25^2 * 2^25 = 625 * 33,554,432 = ~21 billion operations. Infeasible on any standard machine.
- The exponential 2^n factor is inherent to TSP (NP-hard). Bitmask DP is the standard exact approach for small n.
You are given an array where every element appears twice except two elements that appear once. Find both unique elements using O(1) space.
You are given an array where every element appears twice except two elements that appear once. Find both unique elements using O(1) space.
- XOR all elements. The result is
a ^ bwhere a and b are the two unique elements. Since a != b, this XOR is non-zero — at least one bit differs between a and b. - Key step: Find any set bit in
a ^ b. The simplest way:diff_bit = xor_all & (-xor_all)(isolates the lowest set bit). This bit is set in one of a or b but not both. - Partition the array into two groups: elements with that bit set, and elements without. Each group contains exactly one unique element (because paired elements have the same bits and go to the same group). XOR each group independently to find a and b.
- Time: O(n). Space: O(1). Two passes through the array (first to compute XOR, second to partition and XOR each group). Can be done in one pass with careful bookkeeping.
- Why this works: The differentiating bit guarantees that a and b land in different groups. All paired elements land in the same group (both copies have the same bit value), so they cancel via XOR within their group.
- Edge case: If the two unique elements differ in only the sign bit (one positive, one negative in two’s complement), the algorithm still works because it operates on bit positions, not values.
- XOR all elements gives
a ^ b ^ c. You cannot directly extract individual values from a three-way XOR because you do not know which bits belong to which element. - One approach: find a bit where at least one pair of the three unique elements differs. Partition into two groups using that bit. One group has one unique element (solve with simple XOR), the other has two (solve with the two-unique-elements technique above).
- This requires careful selection of the differentiating bit and is significantly more complex. In interviews, this is a staff-level follow-up — most interviewers do not expect a complete solution but want to see your reasoning process.
LeetCode Interview Q&A
Four LeetCode-grinder-flavored questions you should be able to answer cold. Each one has a strong-answer framework, full code, senior follow-ups, common wrong answers, and further reading.Q1: Single Number (LC 136) -- why does XOR work, and how would you generalize it?
Q1: Single Number (LC 136) -- why does XOR work, and how would you generalize it?
- State the three XOR axioms:
a ^ a = 0,a ^ 0 = a, XOR is commutative and associative. - Conclude: XORing the entire array regroups duplicates into pairs that cancel, leaving only the unique element.
- Note O(n) time, O(1) space.
- Mention the natural generalizations: LC 137 (every element appears 3 times) and LC 260 (two unique elements).
[4, 1, 2, 1, 2]: 0 ^ 4 = 4, 4 ^ 1 = 5, 5 ^ 2 = 7, 7 ^ 1 = 6, 6 ^ 2 = 4. The unique element survives.a XOR to a, not 0. Use bit-by-bit counting modulo 3, or the elegant two-variable state machine:ones tracks bits seen once mod 3, twos tracks bits seen twice mod 3. After three appearances both reset to 0.a ^ b. Pick any set bit (e.g., diff = xor_all & -xor_all). Partition the array by that bit — a and b land in different groups, every duplicate pair lands in the same group. XOR each group to recover the two values.result. That is what makes it elegant. Compare with hashing, which requires unbounded memory in the worst case.- “Use a hash set” — works but is O(n) space; misses the bit-manipulation insight the question is testing.
- “Sort and find the lone element” — O(n log n) time, mutates input, also misses the insight.
- Using
ones/twosfor the simple LC 136 case — over-engineered.
- LC 137 — Single Number II
- LC 260 — Single Number III
- LC 268 — Missing Number (same XOR-with-range idea)
Q2: Counting Bits (LC 338) -- why is the DP recurrence dp[i] = dp[i & (i-1)] + 1 elegant?
Q2: Counting Bits (LC 338) -- why is the DP recurrence dp[i] = dp[i & (i-1)] + 1 elegant?
- Naive: for each
iin[0, n], run popcount via Brian Kernighan — O(n log n) total. - Better: notice
popcount(i) = popcount(i & (i - 1)) + 1becausei & (i - 1)isiwith one set bit removed. - Equivalent recurrence:
popcount(i) = popcount(i >> 1) + (i & 1). - Both are O(n) total because each
dp[i]is one O(1) lookup plus an addition.
dp[1] = dp[0] + 1 = 1dp[2] = dp[0] + 1 = 1dp[3] = dp[2] + 1 = 2dp[4] = dp[0] + 1 = 1dp[5] = dp[4] + 1 = 2
dp[i] lookup walks one bit closer to zero — the reason this is O(n) and not O(n log n).[bin(i).count('1') for i in range(n + 1)] works in Python and runs in roughly O(n log n). Most languages have a hardware popcount instruction (Integer.bitCount in Java, __builtin_popcount in C++). The interviewer asking for the DP version is testing whether you can derive the recurrence; saying “use the builtin” misses the point.__builtin_popcountll — modern x86-64 has a single POPCNT instruction.- Calling
bin(i).count('1')inside the loop and claiming O(n) — string conversion is O(log n). - Returning
dp[n]instead of the entire array — misreading the problem. - Off-by-one on the array size (allocating
ninstead ofn + 1).
- LC 191 — Number of 1 Bits (single integer popcount)
- LC 461 — Hamming Distance (XOR plus popcount)
- LC 477 — Total Hamming Distance (per-bit counting)
Q3: Iterate over all subsets of a set using a bitmask -- explain the trick and where it appears.
Q3: Iterate over all subsets of a set using a bitmask -- explain the trick and where it appears.
- Represent the set as a bitmask
maskof width n. - To enumerate all
2^nsubsets: iterateifrom 0 to(1 << n) - 1. Eachiis a subset. - To enumerate subsets of a specific mask (not the full universe): use
sub = (sub - 1) & mask, starting fromsub = mask, untilsubrolls over to 0. - Total cost is exactly
2^popcount(mask)iterations — proportional to the number of subsets, not the universe.
nums = [1, 2, 3], masks 0..7 each produce a distinct subset: 000 -> [], 001 -> [1], 010 -> [2], 011 -> [1,2], …, 111 -> [1,2,3].Why subset-of-mask matters: in bitmask DP (e.g., LC 1125 Smallest Sufficient Team, sum-over-subsets DP) you often need to iterate every subset of a given state. The naive double loop for mask in range(1<<n): for sub in range(1<<n): if (sub & mask) == sub is O(4^n). The (sub - 1) & mask trick is O(3^n) total across all masks (each ordered pair (mask, sub) with sub a subset of mask is visited exactly once).n bits, three states are possible across the (mask, sub) pair: bit is in neither, bit is in mask only, bit is in both. That gives 3^n total ordered pairs, which is exactly what the trick visits.2^n (say n equals 40)?Meet-in-the-middle: split the set in half, enumerate 2^{n/2} subsets on each side, combine with hashing or sorting. Bumps the practical limit from around 20 to around 40. See LC 1755 Closest Subsequence Sum.- Using backtracking and claiming it is O(2^n) better — both are O(n * 2^n); bitmask just removes recursion overhead.
- Forgetting
mask = 0(the empty subset) — LC 78 expects it. - Confusing
(mask >> i) & 1(read bit i) with(mask & i)(mask intersect i, very different).
- LC 78 / LC 90 — Subsets, Subsets II
- LC 1125 — Smallest Sufficient Team (bitmask DP)
- LC 691 — Stickers to Spell Word (bitmask BFS)