203 lines
11 KiB
YAML
203 lines
11 KiB
YAML
title: Binary Watch
|
||
slug: binary-watch
|
||
difficulty: easy
|
||
leetcode_id: 401
|
||
leetcode_url: https://leetcode.com/problems/binary-watch/
|
||
categories:
|
||
- math
|
||
- recursion
|
||
patterns:
|
||
- slug: backtracking
|
||
is_optimal: true
|
||
|
||
function_signature: "def read_binary_watch(turned_on: int) -> list[str]:"
|
||
|
||
test_cases:
|
||
visible:
|
||
- input: { turned_on: 1 }
|
||
expected: ["0:01", "0:02", "0:04", "0:08", "0:16", "0:32", "1:00", "2:00", "4:00", "8:00"]
|
||
- input: { turned_on: 9 }
|
||
expected: []
|
||
hidden:
|
||
- input: { turned_on: 0 }
|
||
expected: ["0:00"]
|
||
- input: { turned_on: 2 }
|
||
expected: ["0:03", "0:05", "0:06", "0:09", "0:10", "0:12", "0:17", "0:18", "0:20", "0:24", "0:33", "0:34", "0:36", "0:40", "0:48", "1:01", "1:02", "1:04", "1:08", "1:16", "1:32", "2:01", "2:02", "2:04", "2:08", "2:16", "2:32", "3:00", "4:01", "4:02", "4:04", "4:08", "4:16", "4:32", "5:00", "6:00", "8:01", "8:02", "8:04", "8:08", "8:16", "8:32", "9:00", "10:00"]
|
||
- input: { turned_on: 10 }
|
||
expected: []
|
||
- input: { turned_on: 8 }
|
||
expected: ["7:31", "7:47", "7:55", "7:59", "11:31", "11:47", "11:55", "11:59"]
|
||
- input: { turned_on: 5 }
|
||
expected: ["0:31", "0:47", "0:55", "0:59", "1:15", "1:23", "1:27", "1:29", "1:30", "1:39", "1:43", "1:45", "1:46", "1:51", "1:53", "1:54", "1:57", "1:58", "2:15", "2:23", "2:27", "2:29", "2:30", "2:39", "2:43", "2:45", "2:46", "2:51", "2:53", "2:54", "2:57", "2:58", "3:07", "3:11", "3:13", "3:14", "3:19", "3:21", "3:22", "3:25", "3:26", "3:28", "3:35", "3:37", "3:38", "3:41", "3:42", "3:44", "3:49", "3:50", "3:52", "3:56", "4:15", "4:23", "4:27", "4:29", "4:30", "4:39", "4:43", "4:45", "4:46", "4:51", "4:53", "4:54", "4:57", "4:58", "5:07", "5:11", "5:13", "5:14", "5:19", "5:21", "5:22", "5:25", "5:26", "5:28", "5:35", "5:37", "5:38", "5:41", "5:42", "5:44", "5:49", "5:50", "5:52", "5:56", "6:07", "6:11", "6:13", "6:14", "6:19", "6:21", "6:22", "6:25", "6:26", "6:28", "6:35", "6:37", "6:38", "6:41", "6:42", "6:44", "6:49", "6:50", "6:52", "6:56", "7:03", "7:05", "7:06", "7:09", "7:10", "7:12", "7:17", "7:18", "7:20", "7:24", "7:33", "7:34", "7:36", "7:40", "7:48", "8:15", "8:23", "8:27", "8:29", "8:30", "8:39", "8:43", "8:45", "8:46", "8:51", "8:53", "8:54", "8:57", "8:58", "9:07", "9:11", "9:13", "9:14", "9:19", "9:21", "9:22", "9:25", "9:26", "9:28", "9:35", "9:37", "9:38", "9:41", "9:42", "9:44", "9:49", "9:50", "9:52", "9:56", "10:07", "10:11", "10:13", "10:14", "10:19", "10:21", "10:22", "10:25", "10:26", "10:28", "10:35", "10:37", "10:38", "10:41", "10:42", "10:44", "10:49", "10:50", "10:52", "10:56", "11:03", "11:05", "11:06", "11:09", "11:10", "11:12", "11:17", "11:18", "11:20", "11:24", "11:33", "11:34", "11:36", "11:40", "11:48"]
|
||
|
||
description: |
|
||
A binary watch has 4 LEDs on the top to represent the hours (0-11), and 6 LEDs on the bottom to represent the minutes (0-59). Each LED represents a zero or one, with the least significant bit on the right.
|
||
|
||
For example, if 3 LEDs are on showing binary values `100` for hours and `110011` for minutes, the watch reads `"4:51"`.
|
||
|
||
Given an integer `turnedOn` which represents the number of LEDs that are currently on, return *all possible times the watch could represent*. You may return the answer in **any order**.
|
||
|
||
The hour must not contain a leading zero.
|
||
|
||
- For example, `"01:00"` is not valid. It should be `"1:00"`.
|
||
|
||
The minute must consist of two digits and may contain a leading zero.
|
||
|
||
- For example, `"10:2"` is not valid. It should be `"10:02"`.
|
||
|
||
constraints: |
|
||
- `0 <= turnedOn <= 10`
|
||
|
||
examples:
|
||
- input: "turnedOn = 1"
|
||
output: '["0:01","0:02","0:04","0:08","0:16","0:32","1:00","2:00","4:00","8:00"]'
|
||
explanation: "With exactly 1 LED on, we can represent hours 1, 2, 4, or 8 (single bits in the 4-bit hour display), or minutes 1, 2, 4, 8, 16, or 32 (single bits in the 6-bit minute display)."
|
||
- input: "turnedOn = 9"
|
||
output: "[]"
|
||
explanation: "With 9 LEDs on out of 10 total, no valid time can be formed. The maximum valid hour (11) needs 3 bits and maximum valid minute (59) needs 6 bits, but 9 bits would exceed valid ranges."
|
||
|
||
explanation:
|
||
intuition: |
|
||
Think of this problem as **distributing lit LEDs between two displays**: 4 LEDs for hours and 6 LEDs for minutes.
|
||
|
||
A binary watch is essentially two separate binary counters. The hour display uses 4 bits (representing 0-15, though only 0-11 are valid hours), and the minute display uses 6 bits (representing 0-63, though only 0-59 are valid minutes).
|
||
|
||
The key insight is that **each number's binary representation has a fixed count of 1-bits**. For example:
|
||
- `3` in binary is `11` (two 1-bits)
|
||
- `7` in binary is `111` (three 1-bits)
|
||
- `5` in binary is `101` (two 1-bits)
|
||
|
||
So if we need exactly `k` LEDs to be on, we need to find all combinations where:
|
||
- The hour value has `i` bits set (where `0 <= i <= k`)
|
||
- The minute value has `k - i` bits set
|
||
- The hour is valid (0-11) and the minute is valid (0-59)
|
||
|
||
Rather than generating combinations of which LEDs to turn on (a backtracking approach), we can simply **enumerate all valid times** and count how many bits are set in each. This turns a potentially complex combinatorial problem into a simple iteration.
|
||
|
||
approach: |
|
||
We use a **Bit Counting Enumeration** approach:
|
||
|
||
**Step 1: Enumerate all valid times**
|
||
|
||
- Loop through all possible hours: `0` to `11`
|
||
- For each hour, loop through all possible minutes: `0` to `59`
|
||
- This gives us `12 × 60 = 720` possible times to check
|
||
|
||
|
||
|
||
**Step 2: Count bits for each time**
|
||
|
||
- For each (hour, minute) pair, count the number of 1-bits in both values
|
||
- Use `bin(n).count('1')` or a built-in popcount function
|
||
- If `bits_in_hour + bits_in_minute == turnedOn`, this is a valid answer
|
||
|
||
|
||
|
||
**Step 3: Format and collect results**
|
||
|
||
- Format valid times as `"{hour}:{minute:02d}"` (hour without leading zero, minute with leading zero if needed)
|
||
- Add to the result list
|
||
|
||
|
||
|
||
This approach is elegant because it avoids complex backtracking logic. With only 720 iterations and O(1) bit counting, it's efficient enough for this constrained problem.
|
||
|
||
common_pitfalls:
|
||
- title: Overcomplicating with Backtracking
|
||
description: |
|
||
A natural instinct is to use backtracking to choose which of the 10 LEDs to turn on. While this works, it's unnecessarily complex.
|
||
|
||
With 10 LEDs and choosing `k` of them, you'd generate C(10, k) combinations, then map each combination back to a time value and validate it.
|
||
|
||
The enumeration approach is simpler: just check all 720 valid times directly. Sometimes brute force on a small domain is the cleanest solution.
|
||
wrong_approach: "Backtracking through LED combinations"
|
||
correct_approach: "Enumerate all valid times and count bits"
|
||
|
||
- title: Forgetting Output Format Requirements
|
||
description: |
|
||
The problem has specific formatting rules:
|
||
- Hours must NOT have a leading zero: `"1:00"` not `"01:00"`
|
||
- Minutes MUST be two digits: `"1:05"` not `"1:5"`
|
||
|
||
Forgetting these will cause wrong answers even if your logic is correct. Use format specifiers like `f"{hour}:{minute:02d}"` to ensure correct padding.
|
||
wrong_approach: "Using consistent padding for both"
|
||
correct_approach: "No padding for hours, zero-padding for minutes"
|
||
|
||
- title: Invalid Hour/Minute Ranges
|
||
description: |
|
||
The 4-bit hour display can represent 0-15, but only 0-11 are valid hours. The 6-bit minute display can represent 0-63, but only 0-59 are valid minutes.
|
||
|
||
If using bit manipulation to generate values, ensure you filter out invalid ranges like hour=12 or minute=60.
|
||
wrong_approach: "Accepting all values the bits can represent"
|
||
correct_approach: "Filter to valid hour (0-11) and minute (0-59) ranges"
|
||
|
||
key_takeaways:
|
||
- "**Enumeration over generation**: When the search space is small (720 times), iterating through all possibilities is often simpler than generating combinations"
|
||
- "**Bit counting as a filter**: The `popcount` operation (counting 1-bits) is a powerful way to filter numbers by their binary properties"
|
||
- "**Problem reframing**: Reframing 'which LEDs to turn on' as 'which times have the right bit count' dramatically simplifies the solution"
|
||
- "**Format strings matter**: Pay close attention to output format requirements — they're easy to overlook but cause wrong answers"
|
||
|
||
time_complexity: "O(1). We check exactly 12 × 60 = 720 possible times, and bit counting is O(1) for fixed-size integers. The complexity is constant regardless of input."
|
||
space_complexity: "O(1). The output list size is bounded by 720 (all possible times), and we use no auxiliary data structures. Since the maximum output is constant, space is O(1)."
|
||
|
||
solutions:
|
||
- approach_name: Bit Counting Enumeration
|
||
is_optimal: true
|
||
code: |
|
||
def read_binary_watch(turned_on: int) -> list[str]:
|
||
result = []
|
||
|
||
# Enumerate all valid hours (0-11) and minutes (0-59)
|
||
for hour in range(12):
|
||
for minute in range(60):
|
||
# Count total bits set in both hour and minute
|
||
# bin(n).count('1') counts the number of 1-bits
|
||
if bin(hour).count('1') + bin(minute).count('1') == turned_on:
|
||
# Format: hour without leading zero, minute with 2 digits
|
||
result.append(f"{hour}:{minute:02d}")
|
||
|
||
return result
|
||
explanation: |
|
||
**Time Complexity:** O(1) — Fixed 720 iterations regardless of input.
|
||
|
||
**Space Complexity:** O(1) — Output bounded by constant 720 possible times.
|
||
|
||
We iterate through every valid (hour, minute) combination and use bit counting to check if the total number of lit LEDs matches `turned_on`. The formatting ensures hours have no leading zeros while minutes are always two digits.
|
||
|
||
- approach_name: Backtracking
|
||
is_optimal: false
|
||
code: |
|
||
def read_binary_watch(turned_on: int) -> list[str]:
|
||
result = []
|
||
# LED values: first 4 are hours (8,4,2,1), last 6 are minutes (32,16,8,4,2,1)
|
||
leds = [8, 4, 2, 1, 32, 16, 8, 4, 2, 1]
|
||
|
||
def backtrack(index: int, remaining: int, hour: int, minute: int):
|
||
# Base case: used all required LEDs
|
||
if remaining == 0:
|
||
if hour < 12 and minute < 60:
|
||
result.append(f"{hour}:{minute:02d}")
|
||
return
|
||
|
||
# Pruning: not enough LEDs left
|
||
if index >= len(leds) or len(leds) - index < remaining:
|
||
return
|
||
|
||
# Choice 1: turn on this LED
|
||
if index < 4:
|
||
backtrack(index + 1, remaining - 1, hour + leds[index], minute)
|
||
else:
|
||
backtrack(index + 1, remaining - 1, hour, minute + leds[index])
|
||
|
||
# Choice 2: skip this LED
|
||
backtrack(index + 1, remaining, hour, minute)
|
||
|
||
backtrack(0, turned_on, 0, 0)
|
||
return result
|
||
explanation: |
|
||
**Time Complexity:** O(2^10) — In the worst case, we explore all subsets of 10 LEDs, though pruning reduces this significantly.
|
||
|
||
**Space Complexity:** O(k) — Recursion depth is at most 10 (number of LEDs).
|
||
|
||
This approach explicitly models choosing which LEDs to turn on. We use backtracking to try including or excluding each LED, building up hour and minute values. While correct, it's more complex than the enumeration approach for this problem size.
|