Counting Bits
Counting Bits: build bits[0..n] in O(n) by reusing bits[i >> 1] instead of recomputing each popcount from scratch.
- 0 <= n <= 10⁵
Intuition
Counting bits returns the number of set bits for every integer from 0 to n. Calling a popcount routine on each value works and is O(n log n), but the problem is really asking for the DP relation that makes it O(n).
The insight is that each number's bit count can be built from a smaller number already computed. Two relations both achieve this.
The cleaner one uses the right shift. Dropping the lowest bit of i gives i >> 1, a strictly smaller number whose count is already known, and the discarded bit is recoverable:
- dp[i] = dp[i >> 1] + (i & 1) — the bits of i are the bits of i without its last bit, plus that last bit.
Every term on the right is already computed when i is reached, so a single forward loop fills the table.
The alternative uses i & (i − 1), which clears the lowest set bit. That produces a smaller number with exactly one fewer bit, giving dp[i] = dp[i & (i-1)] + 1. Both are O(n); the shift version tends to read more clearly.
A third relation appears in many solutions — dp[i] = dp[i − powerOfTwo] + 1, tracking the largest power of two not exceeding i. It works, but requires maintaining that power separately, which the other two avoid entirely.
The base case is dp[0] = 0, since zero has no set bits. Everything else follows from it.
The follow-up asks for a single pass without built-in popcount, which is exactly what these relations deliver.
Counting bits reuses smaller answers: i >> 1 drops the last bit, so i has the same set bits as i / 2 plus its own final bit. That gives bits[i] = bits[i >> 1] + (i & 1) — every answer reuses one already computed, making the whole array linear.
Approach
Before reading on: price up what the direct approach costs here, then ask which operator makes the unwanted values cancel themselves out. Aim for O(n) time and O(n) for the output (O(1) extra beyond it) space.
Reject the per-number count
Calling popcount on each value is O(n log n) and ignores the point. Each number's answer can be built from a smaller number already computed, which is the relation being asked for.
Use the right-shift relation
dp[i] = dp[i >> 1] + (i & 1). Shifting drops the last bit and i & 1 restores it — the bits of i are the bits of i >> 1 plus its final bit.
Confirm the ordering works
i >> 1 is strictly smaller than i, so its value is already filled when i is reached. A single forward loop suffices with no recursion.
Set the base case
dp[0] = 0, since zero has no set bits. Every other entry derives from it through the relation.
Know the clear-lowest-bit alternative
i & (i - 1) clears the lowest set bit, giving dp[i] = dp[i & (i-1)] + 1. Same O(n) cost — useful to recognise, since it appears across many bit-manipulation problems.
Cost of the tabulation
One pass with O(1) work per number gives O(n) time and O(n) space for the output array, which the problem requires anyway.
Solution & live demo
Common pitfalls
Counting each number independently
bits[i] = bin(i).count('1')bits[i] = bits[i >> 1] + (i & 1)
That's O(n log n) and ignores the overlap between answers. Since i >> 1 < i, its count is already stored — one array read replaces the whole popcount.
Using i - 1 as the subproblem
bits[i] = bits[i - 1] + 1
bits[i] = bits[i >> 1] + (i & 1)
Consecutive integers have no simple bit-count relationship — 7 has three set bits and 8 has one. Halving is the operation with a clean recurrence.
Sizing the array to n
bits = [0] * n
bits = [0] * (n + 1)
The output covers 0 through n inclusive, which is n + 1 entries. Sizing to n drops the final answer and throws on the last write.
Edge cases
Result is just [0]; the loop from 1 to n never runs.
i >> 1 has no set bits contributed beyond the trailing 1 that gets added back, so bits[i] = 1, correctly matching a single set bit.
(i & 1) is 1 for odd i and 0 for even i, which is exactly whether the dropped bit was set.
Still O(n) total work and O(n) space for the output array; no per-number popcount loop is ever run.