License Plate — Map Sequential Number to Plate String
Role: Software Engineer
Frequency: Reported (technical round; multi-part with follow-ups)
Problem Overview
The DMV issues plates in a fixed pattern — originally AA000, AA001, …, ZZ999 (two letters followed by three digits). Given a sequential index n, return the plate string that corresponds to it:
0 → AA0001 → AA0012 → AA002- …
999 → AA9991000 → AB000- …
You're given two helpers you may use:
alpha(n)— returnsnin base-26 as uppercase letters (0 → "A",1 → "B", …,25 → "Z",26 → "BA").pad(width, padding, value)— left-padsvaluewithpaddingtowidthcharacters.pad(8, '0', '107') → '00000107'.
Basic Solution (Fixed AA000 Format)
def num_to_plate(n: int) -> str:
# digits portion: last 3 chars, zero-padded
digits = pad(3, '0', str(n % 1000))
# letter portion: advance every 1000 sequential numbers
letters = alpha(n // 1000)
letters = pad(2, 'A', letters) # pad to 2 chars with 'A'
return letters + digitsThe key insight is that the letters and digits are independent counters advancing at different rates: the digit suffix wraps every 1000, which bumps the letter prefix. alpha(n // 1000) gives the letter string; pad to exactly two letters with leading A handles the single-letter case (alpha(0) → "A" becomes "AA").
Follow-up 1 — What If n Overflows the Plate Format?
AA000–ZZ999 has 26² · 10³ = 676,000 plates. Beyond that, the original format is exhausted. Options:
- Return
None/ throw. Simple; leaves policy to the caller. - Grow the prefix.
AAA000— but that changes the format. - Increment the letter prefix.
BA000, etc. — requires generalizingalphato unbounded length.
In interviews, state your chosen policy up-front and move on. None is fine; the interviewer is checking that you noticed the overflow.
Follow-up 2 — General Format: m Letters + d Digits
The plate format becomes configurable. The DMV issues plates in lexicographic cycles:
| Pattern | Letters | Digits | Range example |
|---|---|---|---|
00000 | 0 | 5 | 0 → 00000, …, 99999 |
A0000 | 1 | 4 | 0 → A0000, …, Z9999 |
AA000 | 2 | 3 | 0 → AA000, …, ZZ999 |
AAA00 | 3 | 2 | 0 → AAA00, …, ZZZ99 |
AAAA0 | 4 | 1 | 0 → AAAA0, …, ZZZZ9 |
AAAAA | 5 | 0 | 0 → AAAAA, …, ZZZZZ |
The system issues plates in order (m=0, d=5), then (m=1, d=4), then (m=2, d=3), etc. Each bucket has 26^m · 10^d plates.
Solution
def solution(n: int) -> str | None:
# Bucket definitions in issuance order
configs = [(0, 5), (1, 4), (2, 3), (3, 2), (4, 1), (5, 0)]
counts = [(26 ** m) * (10 ** d) for m, d in configs]
# Step 1: find which bucket n falls into
total = 0
for (m, d), c in zip(configs, counts):
if n < total + c:
offset = n - total
break
total += c
else:
return None # beyond the last bucket — exhausted
# Step 2: within the bucket, split offset into letter and digit portions
digit_part = pad(d, '0', str(offset % (10 ** d))) if d > 0 else ''
letter_part = pad(m, 'A', alpha(offset // (10 ** d))) if m > 0 else ''
return letter_part + digit_partWhy divide by 10^d? Within a bucket, the digit portion is the fast-moving counter (wraps 10^d times) and the letter portion advances every 10^d plates. Same pattern as the basic version, just generalized.
Key Design Considerations
- Two counters, two rates. The letters and digits are fundamentally two bases running in parallel: letters at rate
10^d, digits at rate 1. This factoring is what keeps the code short. - Bucket boundaries are precomputed. Don't loop
ntimes to find the bucket. Compute the cumulative count of each format and binary-search or linear-scan in O(6) = O(1). - Edge cases for each bucket:
m = 0means no letter prefix (pure digit plate);d = 0means no digit suffix (pure letter plate). Both break if you don't guard withif d > 0/if m > 0. padwith'A'vs'0'. Letter portions pad with'A'becausealpha(0) = "A"naturally — you want"A" → "AA", not"A" → "0A".
Follow-up Questions
- Skipping letters. Real DMVs skip
I,O,Qto avoid confusion with1and0. How does that changealphaand the bucket counts? - Reverse lookup: given a plate string, return the sequential
n. How does this decompose? - Custom alphabet per position. What if letter positions 1 and 2 draw from different subsets (e.g., first letter can't be a vowel)?