393 - UTF-8 Validation
Difficulty: Medium | Pattern: Bit Manipulation | Company tags: Google, Facebook, Amazon
Problem Statement
Given an integer array data representing the data, return whether it is a valid UTF-8 encoding (of any number of characters).
UTF-8 encoding rules:
- 1-byte:
0xxxxxxx(starts with 0) - 2-byte:
110xxxxx 10xxxxxx - 3-byte:
1110xxxx 10xxxxxx 10xxxxxx - 4-byte:
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
Each integer in data represents one byte (only the last 8 bits matter).
Example 1:
Input: data = [197,130,1]
Output: true (2-byte then 1-byte)
Example 2:
Input: data = [235,140,4]
Output: false (3-byte but 3rd byte is 00000100, not 10xxxxxx)
Algorithm Flow
Solution — O(n), O(1)
def validUtf8(data: list[int]) -> bool:
num_bytes = 0 # continuation bytes expected
for byte in data:
byte = byte & 0xFF # only care about last 8 bits
if num_bytes == 0:
if byte >> 7 == 0: # 0xxxxxxx
num_bytes = 0
elif byte >> 5 == 0b110: # 110xxxxx
num_bytes = 1
elif byte >> 4 == 0b1110: # 1110xxxx
num_bytes = 2
elif byte >> 3 == 0b11110: # 11110xxx
num_bytes = 3
else:
return False
else:
if byte >> 6 != 0b10: # must be 10xxxxxx
return False
num_bytes -= 1
return num_bytes == 0
Dry Run
data = [197, 130, 1]
- 197 = 0b11000101 → 197>>5 = 6 = 0b110 → 2-byte, expect 1 continuation
- 130 = 0b10000010 → 130>>6 = 2 = 0b10 → continuation ✓, num_bytes=0
- 1 = 0b00000001 → 1>>7 = 0 → 1-byte ✓
Return num_bytes == 0 → True ✓
Edge Cases
- Trailing continuation byte after sequence → num_bytes=0 and byte starts with
10→ invalid - Incomplete multi-byte sequence → num_bytes != 0 at end → invalid
- 5-byte or longer sequences → pattern doesn't match any valid prefix → return False
Complexity
- Time: O(n)
- Space: O(1)
Key Terms
| Term | Definition |
|---|---|
| Continuation byte | A byte matching 10xxxxxx, valid only as a follow-up byte in a multi-byte sequence. |
| Leading byte | The first byte of a UTF-8 sequence; its high bits encode how many continuation bytes follow. |
| Bit masking | Using &, >> to isolate specific bits (e.g. byte & 0xFF, byte >> 5) to check a prefix pattern. |
| State counter | num_bytes, tracking how many continuation bytes are still expected before the next leading byte. |
FAQ
Q: Why mask each value with 0xFF before checking bits?
A: Input integers may have more than 8 bits set (per the problem, only the last 8 bits matter), so masking discards any extra high-order bits before pattern matching.
Q: What happens if a leading byte's prefix doesn't match any valid pattern (0, 110, 1110, 11110)?
A: The function returns False immediately — no encoding starting with 10xxxxxx or 11111xxx is valid as a leading byte.
Q: Can the array end in the middle of a multi-byte sequence?
A: No — if num_bytes != 0 after processing all bytes, the sequence is incomplete and the answer is False.
Q: How would this change for UTF-16 or a variable byte order? A: The bit-pattern checks would change to match UTF-16's surrogate-pair rules, but the core state-machine approach (leading byte determines expected continuation count) still applies.
Q: Is recursion or a stack needed here?
A: No — a single counter (num_bytes) is sufficient because UTF-8 sequences don't nest; it is a flat state machine.
Quick Revision
- Problem: validate whether an integer array represents valid UTF-8 encoded bytes.
- Each byte only uses its last 8 bits — always mask with
0xFF. - Leading byte prefixes:
0→ 1-byte,110→ 2-byte,1110→ 3-byte,11110→ 4-byte. - Continuation bytes must match
10xxxxxx. - Track
num_bytes= continuation bytes still expected; decrement on each valid continuation byte. - Any prefix that doesn't match a known pattern → return
Falseimmediately. - At the end, valid only if
num_bytes == 0(no incomplete sequence). - Runs in O(n) time and O(1) space — single pass, no extra storage.
Related Problems
- Shares the bit-manipulation/prefix-matching pattern with 191 - Number Of 1 Bits and 342 - Power Of Four.
- Also related in spirit to other "validate against a bit-pattern state machine" problems such as checking binary string codes.