TL;DR
Add column by column from the right with a carry β O(n) time, O(n) space β or fold with XOR/AND carry propagation.
The naive intuition: binary strings are just numbers, so parse both, add, and print back in base 2.
class Solution:
def addBinary(self, a: str, b: str) -> str:
return bin(int(a, 2) + int(b, 2))[2:]
Complexity: O(n) in Python (big-int add), O(1) extra space.
Why the constraints matter: this leans entirely on arbitrary-precision integers. With 10^4-bit inputs, a fixed-width int/long (32 or 64 bits) in most languages overflows long before you get here, which is exactly why interviewers ask for the manual version below.
Approach 2 β Column addition with a carry
The insight: you never need the whole number at once. Walk both strings from the least-significant end, keep a running carry, and at each column compute total = carry + bit_a + bit_b. The output bit is total & 1 (is the sum odd?) and the carry into the next column is total >> 1 (was it 2 or 3?).
class Solution:
def addBinary(self, a: str, b: str) -> str:
i, j = len(a) - 1, len(b) - 1
carry = 0
out = []
while i >= 0 or j >= 0 or carry:
total = carry
if i >= 0:
total += int(a[i])
i -= 1
if j >= 0:
total += int(b[j])
j -= 1
out.append(str(total & 1)) # sum bit for this column
carry = total >> 1 # carry into the next column
return "".join(reversed(out))
Walkthrough on a = "1010", b = "1011" (adding right to left):
| step | a bit | b bit | carry in | total | out bit | carry out |
|---|
| 1 | 0 | 1 | 0 | 1 | 1 | 0 |
| 2 | 1 | 1 | 0 | 2 | 0 | 1 |
| 3 | 0 | 0 | 1 | 1 | 1 | 0 |
| 4 | 1 | 1 | 0 | 2 | 0 | 1 |
| 5 | β | β | 1 | 1 | 1 | 0 |
Collected bits are 1,0,1,0,1; reversed β "10101" = 21. β
Complexity: O(n) time where n = max(len(a), len(b)), O(n) space for the output.
Approach 3 β XOR/AND carry propagation
The insight: addition on bit-vectors splits cleanly into two halves. x ^ y produces the sum in every column ignoring carries; x & y marks columns where both bits are 1, i.e. where a carry is generated, and shifting that left by one (<< 1) drops each carry into the next column. Re-add those two results and repeat; the carry word shrinks toward zero, and when it hits 0 the XOR already holds the full sum.
class Solution:
def addBinary(self, a: str, b: str) -> str:
x, y = int(a, 2), int(b, 2)
while y: # y holds the pending carries
answer = x ^ y # add columns, ignore carry
carry = (x & y) << 1 # carries, moved one column left
x, y = answer, carry
return bin(x)[2:]
Walkthrough on x = 10 (1010), y = 11 (1011):
answer = 1010 ^ 1011 = 0001; carry = (1010 & 1011) << 1 = 1010 << 1 = 10100. Now x=00001, y=10100.
answer = 00001 ^ 10100 = 10101; carry = (00001 & 10100) << 1 = 0. Now x=10101, y=0 β stop.
- Result
bin(21)[2:] = "10101". β
Complexity: O(n) per pass and O(n) passes in the worst case of a rippling carry, so O(n^2) bit operations here β its value is conceptual (this is how a hardware adder thinks), not speed.
Common pitfalls
- Dropping the final carry: the loop must keep going while
carry (or y) is nonzero, or "11" + "1" loses the leading 1 and returns "00".
- Reversing at the wrong time: you build the answer least-significant bit first, so reverse (or
insert(0, ...)) before joining.
- Assuming equal lengths: guard each index independently (
if i >= 0), since the strings can differ in length.
[2:] slice: bin(4) is "0b100"; forgetting to strip the "0b" prefix returns a malformed answer.
Pattern takeaway
Any addition decomposes into βsum without carryβ (XOR) plus βcarries, shifted one place leftβ (AND then << 1). Whether you simulate columns with a carry variable or fold whole words with XOR/AND, the mental model is the same β and it is the bridge from arithmetic to pure bit manipulation.