A clear explanation of determining whether the last character must be a one-bit character using greedy parsing.
Problem Restatement
We are given a binary array:
bitsThe array encodes characters using two rules:
| Character Type | Encoding |
|---|---|
| One-bit character | 0 |
| Two-bit character | 10 or 11 |
The array always ends with:
0We need to determine whether the final character must be a one-bit character.
Return:
Trueif the last character is one-bit.
Otherwise return:
FalseInput and Output
| Item | Meaning |
|---|---|
| Input | Binary array bits |
| Output | Whether the last character is one-bit |
| One-bit encoding | 0 |
| Two-bit encoding | 10 or 11 |
| Important detail | The array always ends with 0 |
The function shape is:
class Solution:
def isOneBitCharacter(self, bits: list[int]) -> bool:
...Examples
Example 1:
bits = [1, 0, 0]Parse from left to right:
10 -> two-bit character
0 -> one-bit characterThe final character is one-bit.
Output:
TrueExample 2:
bits = [1, 1, 1, 0]Parse from left to right:
11 -> two-bit character
10 -> two-bit characterThe final 0 belongs to the last two-bit character.
So the final character is not one-bit.
Output:
FalseFirst Thought: Try Every Parsing
A recursive approach could try every possible valid decoding.
At each index:
| Current Bit | Possible Action |
|---|---|
0 | Take one-bit character |
1 | Take two-bit character |
Then recursively check whether the final character is one-bit.
This works, but it is unnecessary.
The encoding rules already force a unique greedy parsing.
Key Insight
The encoding is deterministic.
Whenever we see:
0it must represent a one-bit character.
Whenever we see:
1it must begin a two-bit character, because there is no valid one-bit encoding starting with 1.
So:
| Current Bit | Move |
|---|---|
0 | Advance by 1 |
1 | Advance by 2 |
We only need to simulate decoding until we reach the last position.
If we land exactly on the final index, then the last character is one-bit.
If we jump past the final index, then the last 0 was consumed as part of a two-bit character.
Algorithm
- Start with index
i = 0. - While
i < len(bits) - 1:- If
bits[i] == 0, incrementiby1. - Otherwise increment
iby2.
- If
- Return whether:
i == len(bits) - 1
Correctness
The encoding rules uniquely determine how to parse the array.
If bits[i] == 0, the current character must be the one-bit encoding 0, so advancing by one position is correct.
If bits[i] == 1, the current character must be either 10 or 11, both of which are two-bit encodings, so advancing by two positions is correct.
Therefore the algorithm always follows the only valid decoding path.
The loop stops before processing the final array position directly.
If the index ends exactly at the final position, then the final 0 begins its own one-bit character.
If the index jumps beyond the final position, then the final 0 was consumed as the second bit of a two-bit character.
Thus the algorithm returns True exactly when the last character is one-bit.
Complexity
Let n be the length of bits.
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | We scan the array once |
| Space | O(1) | Only one index variable is used |
Implementation
class Solution:
def isOneBitCharacter(self, bits: list[int]) -> bool:
i = 0
last = len(bits) - 1
while i < last:
if bits[i] == 0:
i += 1
else:
i += 2
return i == lastCode Explanation
The variable:
last = len(bits) - 1stores the final index.
We process characters from left to right:
while i < last:If the current bit is 0, it represents a one-bit character:
if bits[i] == 0:
i += 1If the current bit is 1, it begins a two-bit character:
else:
i += 2After parsing finishes:
return i == lastmeans the last character stands alone as a one-bit character.
Example Walkthrough
Use:
bits = [1, 0, 0]Initialize:
i = 0
last = 2At index 0:
bits[0] = 1So this begins a two-bit character:
10Advance:
i = 2Now:
i == lastSo the final bit forms a one-bit character.
Return:
TrueNow use:
bits = [1, 1, 1, 0]At index 0:
11Advance to:
i = 2At index 2:
10Advance to:
i = 4Now:
i > lastSo the final 0 was part of the two-bit character 10.
Return:
FalseAlternative Observation
Another way to think about the problem:
The final 0 is one-bit if and only if the number of consecutive 1s immediately before it is even.
For example:
[1, 0, 0]There is one 1 before the final 0.
Odd count means the previous 1 pairs with the earlier 0, leaving the last 0 alone.
But:
[1, 1, 1, 0]There are three consecutive 1s before the final 0.
Odd count means the final 0 gets consumed by a two-bit character.
The greedy simulation is usually clearer and simpler.
Testing
def test_is_one_bit_character():
s = Solution()
assert s.isOneBitCharacter([1, 0, 0]) is True
assert s.isOneBitCharacter([1, 1, 1, 0]) is False
assert s.isOneBitCharacter([0]) is True
assert s.isOneBitCharacter([1, 0]) is False
assert s.isOneBitCharacter([0, 0]) is True
assert s.isOneBitCharacter([1, 1, 0]) is True
print("all tests passed")
test_is_one_bit_character()Test coverage:
| Test | Why |
|---|---|
| Standard true example | Confirms final standalone 0 |
| Standard false example | Confirms final 0 consumed by two-bit character |
Single 0 | Smallest valid input |
| Single two-bit character | Confirms no standalone final character |
| Two one-bit characters | Confirms repeated one-bit parsing |
| Mixed encoding | Confirms greedy stepping works |