2166. Design Bitset
On LeetCode ->Problem¶
Implement a bitset initialized with size zeros, supporting:
fix(idx)/unfix(idx): set a bit to1/0.flip(): invert every bit.all()/one(): check whether every / any bit is1.count(): return the number of ones.toString(): return bits in index order.
Operation State Return
Bitset(3) 000 -
fix(1) 010 -
flip() 101 -
unfix(0) 001 -
count() 001 1
toString() 001 "001"
class Bitset:
def __init__(self, size: int):
pass
def fix(self, idx: int) -> None:
pass
def unfix(self, idx: int) -> None:
pass
def flip(self) -> None:
pass
def all(self) -> bool:
pass
def one(self) -> bool:
pass
def count(self) -> int:
pass
def toString(self) -> str:
pass
Key trick¶
Store a global flip flag and a ones count: the logical bit is stored_bit ^ flipped, so flipping only toggles the flag and replaces the count with size - count.
Trap¶
Repeated fix or unfix must not change the count; check the logical bit, especially after a flip. Physically inverting all bits makes frequent flips too expensive.
Why is it interesting?¶
It demonstrates lazy updates: change how stored data is interpreted instead of modifying every element.
Python solution¶
class Bitset:
def __init__(self, size: int):
self.bits = [0] * size
self.flipped = 0
self.cnt = 0
def fix(self, idx: int) -> None:
# Only change a logical zero.
if self.bits[idx] ^ self.flipped == 0:
self.bits[idx] ^= 1
self.cnt += 1
def unfix(self, idx: int) -> None:
# Only change a logical one.
if self.bits[idx] ^ self.flipped == 1:
self.bits[idx] ^= 1
self.cnt -= 1
def flip(self) -> None:
# Stored bits stay unchanged; their interpretation reverses.
self.flipped ^= 1
self.cnt = len(self.bits) - self.cnt
def all(self) -> bool:
return self.cnt == len(self.bits)
def one(self) -> bool:
return self.cnt > 0
def count(self) -> int:
return self.cnt
def toString(self) -> str:
return "".join(str(x ^ self.flipped) for x in self.bits)
Time: Initialization and toString() take \(O(n)\); every other operation takes \(O(1)\).
Space: \(O(n)\) storage; toString() additionally uses \(O(n)\) space.
Comment on my solution¶
Your solution is functionally correct; the timeout comes from flip(), which constructs a full set and costs \(O(n)\) per call, giving \(O(qn)\) time for \(q\) flips.
The sparse-set idea is valid, but flipping can make the set dense. A flip flag would let the set represent stored ones rather than logical ones, preserving average \(O(1)\) updates and flips.
# Ideas:
# - It reminds me of problem about sparse vector where almost all
# element are zeros so we could keep only map (index->val) for value
# that are not 0
# WRONG
# Time Limit Exceeded: 44/48 testcases passed
class Bitset:
def __init__(self, size: int):
self.size = size
self.ones = set() # set of index with set bit
def fix(self, idx: int) -> None:
self.ones.add(idx)
def unfix(self, idx: int) -> None:
self.ones.discard(idx)
def flip(self) -> None:
self.ones = set(range(self.size)) - self.ones
def all(self) -> bool:
return len(self.ones) == self.size
def one(self) -> bool:
return len(self.ones) > 0
def count(self) -> int:
return len(self.ones)
def toString(self) -> str:
bits = [0] * self.size
for i in self.ones:
bits[i] = 1
return "".join(map(str, bits))