Files
codetutor/backend/data/questions/one-bit-and-two-bit-characters.yaml

169 lines
7.7 KiB
YAML

title: 1-bit and 2-bit Characters
slug: one-bit-and-two-bit-characters
difficulty: easy
leetcode_id: 717
leetcode_url: https://leetcode.com/problems/1-bit-and-2-bit-characters/
categories:
- arrays
patterns:
- slug: greedy
is_optimal: true
function_signature: "def is_one_bit_character(bits: list[int]) -> bool:"
test_cases:
visible:
- input: { bits: [1, 0, 0] }
expected: true
- input: { bits: [1, 1, 1, 0] }
expected: false
hidden:
- input: { bits: [0] }
expected: true
- input: { bits: [0, 0] }
expected: true
- input: { bits: [1, 0] }
expected: false
- input: { bits: [1, 1, 0] }
expected: true
- input: { bits: [0, 1, 0] }
expected: false
description: |
We have two special characters:
- The first character can be represented by one bit `0`.
- The second character can be represented by two bits (`10` or `11`).
Given a binary array `bits` that ends with `0`, return `true` if the last character must be a one-bit character.
constraints: |
- `1 <= bits.length <= 1000`
- `bits[i]` is either `0` or `1`.
examples:
- input: "bits = [1,0,0]"
output: "true"
explanation: "The only way to decode it is two-bit character (10) and one-bit character (0). So the last character is one-bit character."
- input: "bits = [1,1,1,0]"
output: "false"
explanation: "The only way to decode it is two-bit character (11) and two-bit character (10). So the last character is NOT a one-bit character."
explanation:
intuition: |
Imagine you're reading a coded message where each character is either a single `0` or a pair starting with `1` (`10` or `11`). Your job is to determine if the very last `0` in the array stands alone as its own character.
The key insight is that **a `1` always consumes the next bit**. When you see a `1`, you must read two bits together as one character. When you see a `0`, it stands alone as a one-bit character.
Think of it like this: walk through the array from left to right. Every time you encounter a `1`, skip ahead by 2 positions (you've consumed a two-bit character). Every time you encounter a `0`, skip ahead by 1 position (you've consumed a one-bit character).
If this greedy decoding process lands you exactly on the last index, then that final `0` must be a standalone one-bit character. If you land past it (by skipping over it as part of a two-bit character), the answer is `false`.
approach: |
We solve this using a **Greedy Linear Scan**:
**Step 1: Initialise the position pointer**
- `i`: Set to `0` to start at the beginning of the array
&nbsp;
**Step 2: Iterate through the array (except the last element)**
- While `i < len(bits) - 1` (we stop before the last element):
- If `bits[i] == 1`: This starts a two-bit character, so jump ahead by 2 (`i += 2`)
- If `bits[i] == 0`: This is a one-bit character, so jump ahead by 1 (`i += 1`)
&nbsp;
**Step 3: Check the final position**
- After the loop, check if `i == len(bits) - 1`
- If true, we landed exactly on the last bit, meaning it's a standalone one-bit character
- If false (we landed past the last index), the last `0` was consumed as part of a two-bit character
&nbsp;
This greedy approach works because the decoding is **deterministic** — there's only one valid way to decode any given sequence, and a `1` always forces a two-bit read.
common_pitfalls:
- title: Trying to Decode Backwards
description: |
It might seem intuitive to start from the end and work backwards, but this approach is tricky because characters are defined by their **first** bit, not their last.
For example, in `[1, 0, 0]`, working backwards you'd see a `0` and wonder if it's standalone or the second half of `10`. The encoding is designed to be read forward, so decode forward.
wrong_approach: "Iterating from the end of the array"
correct_approach: "Iterate from the start, following the encoding rules"
- title: Counting Ones Before the Last Zero
description: |
Some solutions try to count consecutive `1`s before the final `0` and check if the count is odd or even. While this can work, it's error-prone and less intuitive than simulating the actual decoding process.
The simulation approach directly models the problem and is harder to get wrong.
wrong_approach: "Counting 1s and using modular arithmetic"
correct_approach: "Simulate the decoding by jumping 1 or 2 positions"
- title: Off-by-One Errors
description: |
A common mistake is iterating until `i < len(bits)` instead of `i < len(bits) - 1`. This causes the loop to potentially decode the last element, which defeats the purpose of checking whether we land on it.
We must stop **before** the last element to see if our decoding naturally lands on it.
wrong_approach: "Loop while i < len(bits)"
correct_approach: "Loop while i < len(bits) - 1"
key_takeaways:
- "**Greedy simulation**: When a sequence has deterministic decoding rules, simulate the process step by step"
- "**Follow the encoding direction**: This encoding is designed to be read left-to-right; decode in the same direction"
- "**Jump patterns**: Recognise when elements dictate variable-length jumps (1 consumes 2 positions, 0 consumes 1)"
- "**Boundary condition**: The key insight is checking WHERE you land after decoding, not WHAT you decode"
time_complexity: "O(n). We traverse the array once, visiting each element at most once."
space_complexity: "O(1). We only use a single pointer variable `i`, regardless of input size."
solutions:
- approach_name: Greedy Linear Scan
is_optimal: true
code: |
def is_one_bit_character(bits: list[int]) -> bool:
i = 0
# Decode all characters except potentially the last one
while i < len(bits) - 1:
if bits[i] == 1:
# Two-bit character: skip the next bit too
i += 2
else:
# One-bit character: move to next position
i += 1
# If we land exactly on the last index, it's a one-bit character
return i == len(bits) - 1
explanation: |
**Time Complexity:** O(n) — Single pass through the array.
**Space Complexity:** O(1) — Only one integer variable used.
We greedily decode the array from left to right. A `1` forces us to consume two bits, while a `0` consumes just one. After processing all characters before the last position, we check if we've landed exactly on the final index.
- approach_name: Count Ones Before Last Zero
is_optimal: false
code: |
def is_one_bit_character(bits: list[int]) -> bool:
# Count consecutive 1s immediately before the last 0
ones_count = 0
# Start from second-to-last element, go backwards
for i in range(len(bits) - 2, -1, -1):
if bits[i] == 1:
ones_count += 1
else:
break
# If even number of 1s, they pair up, leaving last 0 alone
# If odd number of 1s, the last 1 pairs with the final 0
return ones_count % 2 == 0
explanation: |
**Time Complexity:** O(n) — Worst case, all elements before the last are `1`.
**Space Complexity:** O(1) — Only a counter variable.
This approach counts consecutive `1`s immediately preceding the final `0`. If the count is even, all `1`s pair up with each other (forming `11` characters), leaving the last `0` standalone. If odd, one `1` must pair with the final `0` (forming `10`), making the last character two-bit. While correct, this is less intuitive than simulating the decoding process.