401. Binary Watch

EasyBit ManipulationBacktracking
Leetcode Link

Problem Description

A binary watch displays the time using binary representation with LEDs. A unique feature of this watch is that it has separate sets of LEDs for hours and minutes. The top set of 4 LEDs represent the hours (0 through 11), and the bottom set of 6 LEDs represent the minutes (0 through 59). Each LED signifies a binary digit (bit) with values 0 or 1, with the least significant bit being on the far right. As in any binary number, the bits are weighted based on their position, doubling from right to left (1, 2, 4, 8, etc.). When a certain number of LEDs are turned on, the problem requires us to find all the possible times that configuration can represent on a binary watch.

There are a few rules to keep in mind:

  • The hour representation does not have a leading zero. So "01:00" is not an acceptable representation; it should be "1:00".
  • The minutes must be two digits, potentially including a leading zero if necessary. For example, "10:2" is not valid; it should be "10:02".

A key task in this problem is to list all the possible times that can be displayed by the watch, given the number of LEDs that are lit, represented by the integer turnedOn.

Intuition

To solve this problem, we approach it by simulating the behavior of the binary watch:

  1. We generate all possible combinations of the hour and minute by iterating through their maximum possible values, which are 11 for hours (from 0 to 11) and 59 for minutes (from 0 to 59).
  2. For each generated hour and minute combination, we convert them to their binary representation and concatenate the strings.
  3. The combined string of binary representation for both hour and minute is then checked for the number of '1's it contains. If it matches the turnedOn value, it means this time combination has the correct number of LEDs lit.
  4. We add the valid time combinations formatted correctly, "{hours}:{minutes}", ensuring minutes are always two digits (padded with leading zero if necessary).
  5. The final step is to return all the valid formatted times.

Understanding that the processes of counting lit LEDs and formatting strings are separate, yet equally important parts of the solution enables us to effectively iterate over possible time combinations and filter out the valid ones. This approach efficiently uses Python's list comprehension, formatting, and string operation utilities to come up with a succinct and elegant solution.

Learn more about Backtracking patterns.

Not Sure What to Study? Take the 2-min Quiz to Find Your Missing Piece:

How does quick sort divide the problem into subproblems?

Solution Approach

The solution uses a simple, brute force approach to find all valid times that can be represented on a binary watch with a given number of LEDs turned on. Here's a step-by-step breakdown:

  1. We initialize an empty list that will be used to store the valid time representations as strings.

  2. We utilize two nested for loops, with the outer loop iterating through the possible hours (0 to 11) and the inner loop iterating through the possible minutes (0 to 59). This ensures that we cover every potential combination of hours and minutes.

  3. For each combination of the hour and minute, we first convert them to binary strings using Python's built-in bin() function. This results in strings like '0b10' for the decimal number 2.

  4. We concatenate the binary strings of both the hour and minute and use the string method .count('1') to calculate the total number of '1's, which correspond to the LEDs being on.

  5. We compare the count of '1's with the turnedOn parameter. If they match, it means the current hour and minute combination is one of the valid representations for the given number of LEDs turned on.

  6. For each valid time representation, we format the hour and minute in the string "{:d}:{:02d}".format(i, j) to comply with the required time format (no leading zero for hours and two digits with a leading zero for minutes when necessary).

  7. We append the formatted time string to our list of results.

  8. After iterating through all the possible combinations, the list of valid time representations is complete, and we return it as the final result.

This solution is straightforward since it iterates through all possible representations without the use of complex algorithms or data structures. The use of list comprehensions in Python provides a concise way to pack this entire process into a single line of code while maintaining high readability.

It's worth noting that this brute force solution is feasible because the total number of possible times in a day (12 hours * 60 minutes = 720 possibilities) is small, and therefore, iterating over all of them is not computationally expensive. For a much larger set of possibilities, a more intricate algorithm, perhaps using backtracking or bit manipulation, would have been necessary to keep the computation time practical.

Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

Given a sorted array of integers and an integer called target, find the element that equals to the target and return its index. Select the correct code that fills the ___ in the given code snippet.

1def binary_search(arr, target):
2    left, right = 0, len(arr) - 1
3    while left ___ right:
4        mid = (left + right) // 2
5        if arr[mid] == target:
6            return mid
7        if arr[mid] < target:
8            ___ = mid + 1
9        else:
10            ___ = mid - 1
11    return -1
12
1public static int binarySearch(int[] arr, int target) {
2    int left = 0;
3    int right = arr.length - 1;
4
5    while (left ___ right) {
6        int mid = left + (right - left) / 2;
7        if (arr[mid] == target) return mid;
8        if (arr[mid] < target) {
9            ___ = mid + 1;
10        } else {
11            ___ = mid - 1;
12        }
13    }
14    return -1;
15}
16
1function binarySearch(arr, target) {
2    let left = 0;
3    let right = arr.length - 1;
4
5    while (left ___ right) {
6        let mid = left + Math.trunc((right - left) / 2);
7        if (arr[mid] == target) return mid;
8        if (arr[mid] < target) {
9            ___ = mid + 1;
10        } else {
11            ___ = mid - 1;
12        }
13    }
14    return -1;
15}
16

Example Walkthrough

Let's walk through a small example to illustrate the solution approach using turnedOn = 3.

We will follow the solution steps to find all possible times when exactly 3 LEDs are lit on the binary watch:

  1. Start with an empty list to hold our valid times.

  2. Iterate over possible hours (0 to 11) and minutes (0 to 59):

    For example, if we select hour 3 (011 in binary) and minute 5 (000101 in binary), we count the '1's in the concatenated binary string 011000101.

  3. When we convert these to binary and concatenate we get: '0b1100b101'. Cleaning up the binary representation (removing '0b'), we have '1100101'.

  4. Count the number of '1's in the binary representation: '1100101' has four '1's, which does not match our turnedOn value of 3. So, this combination is skipped.

  5. Continue this process with other combinations:

    • Hour 1 (1 in binary) and minute 10 (1010 in binary) total to 3 '1's when concatenated (11010), making 1:10 a valid time.
    • Hour 4 (100 in binary) and minute 3 (11 in binary) are another example that totals to 3 '1's when concatenated (1000011), giving us another valid time: 4:03.
  6. Continue looping through and only add the combinations that have exactly 3 '1's to the list after formatting them correctly.

  7. After finishing the loop, we will have a list of valid times.

For a turnedOn value of 3, the list might include times like 1:10, 4:03, 0:07, and more, assuming they match the criteria of having exactly 3 LEDs turned on.

  1. Return this list as the final result.

Solution Implementation

1from typing import List
2
3class Solution:
4    def readBinaryWatch(self, num_leds_lit: int) -> List[str]:
5        # This list comprehension will collect all possible time formats
6        time_formats = [
7            '{:d}:{:02d}'.format(hour, minute)
8            for hour in range(12)       # Loop through the 12 hours
9            for minute in range(60)     # Loop through the 60 minutes
10            if (bin(hour) + bin(minute)).count('1') == num_leds_lit
11            # Check if the sum of the bits set to '1' in both the hour's and 
12            # minute's binary representation equals the number of LEDs that are lit
13        ]
14      
15        return time_formats
16
1class Solution {
2    public List<String> readBinaryWatch(int numLEDsOn) {
3        // This list will hold all the possible times the binary watch can represent with numLEDsOn LEDs lit.
4        List<String> possibleTimes = new ArrayList<>();
5
6        // Loop over all possible hours (0-11)
7        for (int hour = 0; hour < 12; ++hour) {
8            // Loop over all possible minutes (0-59)
9            for (int minute = 0; minute < 60; ++minute) {
10                // Combine the number of bits (LEDs) set in both hour and minute
11                // If the total equals numLEDsOn, this is a correct time and should be added to the list
12                if (Integer.bitCount(hour) + Integer.bitCount(minute) == numLEDsOn) {
13                    // Format the time as "h:mm" and add it to the list
14                    possibleTimes.add(String.format("%d:%02d", hour, minute));
15                }
16            }
17        }
18        // Return all the possible times
19        return possibleTimes;
20    }
21}
22
1#include <vector> // Include for using vector
2#include <string> // Include for using string
3#include <bitset> // Include for using bitset
4
5class Solution {
6public:
7    // Defines a method to read a binary watch.
8    // It takes an integer 'turnedOn' to indicate the number of LEDs that are on.
9    vector<string> readBinaryWatch(int turnedOn) {
10        vector<string> ans; // This will hold all possible times the binary watch could represent.
11
12        // Loop over all possible hours (0 to 11).
13        for (int hour = 0; hour < 12; ++hour) {
14            // Loop over all possible minutes (0 to 59).
15            for (int minute = 0; minute < 60; ++minute) {
16                // Check if the total count of bits (LEDs on) for both hour and minute equals 'turnedOn'.
17                if (bitset<10>(hour << 6 | minute).count() == turnedOn) {
18                    // Use a conditional statement to format minutes (add leading zero if less than 10).
19                    string minuteFormatted = minute < 10 ? "0" + std::to_string(minute) : std::to_string(minute);
20
21                    // Construct the time string and add it to the list of answers.
22                    ans.push_back(std::to_string(hour) + ":" + minuteFormatted);
23                }
24            }
25        }
26        return ans; // Return the list of valid times.
27    }
28};
29
1function readBinaryWatch(turnedOn: number): string[] {
2    // Base case: if no LED is turned on, the time is 0:00
3    if (turnedOn === 0) {
4        return ['0:00'];
5    }
6
7    // Initialize the array representing the state of each LED in the watch
8    const ledState = new Array(10).fill(false);
9    // Function to create the time string from the LED state array
10    const createTimeString = () => {
11        // Calculate the hour by interpreting the first 4 bits
12        const hours = ledState.slice(0, 4).reduce((acc, led) => (acc << 1) | Number(led), 0);
13        // Calculate the minutes by interpreting the last 6 bits
14        const minutes = ledState.slice(4).reduce((acc, led) => (acc << 1) | Number(led), 0);
15        // Return them as a tuple
16        return [hours, minutes];
17    };
18    // Helper function that generates time strings given an index and remaining count of LEDs that can be turned on
19    const backtrack = (index: number, count: number) => {
20        // If index plus count exceeds total LEDs, return since we cannot have more turned on LEDs
21        // And if count is zero, we know no more LEDs can be turned on.
22        if (index + count > ledState.length || count === 0) {
23            return;
24        }
25      
26        // Turn on the LED at the current index and attempt to build time
27        ledState[index] = true;
28        if (count === 1) {
29            // If exactly one LED is left to turn on, create and validate the time
30            const [hours, minutes] = createTimeString();
31            if (hours < 12 && minutes < 60) {
32                // Format the time with leading zero for minutes, if needed, and add to results
33                results.push(`${hours}:${minutes < 10 ? '0' + minutes : minutes}`);
34            }
35        }
36        // Recurse for the next LED with one less LED available to turn on
37        backtrack(index + 1, count - 1);
38      
39        // Backtrack: turn off the LED and try next positions
40        ledState[index] = false;
41        backtrack(index + 1, count);
42    };
43
44    // Initialize the results array
45    const results: string[] = [];
46
47    // Call the helper function starting with the first LED
48    backtrack(0, turnedOn);
49  
50    // Return the resulting time strings
51    return results;
52}
53
Not Sure What to Study? Take the 2-min Quiz:

Which of these pictures shows the visit order of a depth-first search?

Time and Space Complexity

The given Python code snippet is a function that generates all possible times displayed on a binary watch, specifically when a certain number of LEDs are turned on. The time complexity and space complexity of the code are analyzed as follows:

Time Complexity

The time complexity is determined by the number of iterations in the nested loops and the operation within the loop:

  • The outer loop runs 12 times (i from 0 to 11) because there are 12 possible hours on a watch.
  • The inner loop runs 60 times (j from 0 to 59) to represent 60 possible minutes.
  • For each combination of i and j, there are operations that convert these integers to binary strings (bin()), concatenate them, and count the number of '1' bits.

As such, the time complexity can be expressed as the product of the number of iterations in the loops and the complexity of the operations within them. Assuming that bin() and .count('1') operations are O(m) and O(n) respectively, where m and n are the number of bits in the hour and minute parts. The hour part has at most 4 bits, and the minute part has at most 6 bits. However, since these bit lengths are fixed and independent of input size, their contribution to complexity is constant.

Therefore, the overall time complexity is O(12 * 60), which simplifies to O(1) — constant time complexity, as the loop bounds do not depend on the size of the input, but on the fixed size of hours and minutes on a binary watch.

Space Complexity

The space complexity is determined by the space needed to store the output and any intermediary data:

  • The list comprehension generates up to a maximum of 12 * 60 time strings (in case turnedOn is 0, which would imply all possible times). Each time is a string, and the length of the strings has an upper bound, so they can be considered to take constant space.
  • Hence, the space complexity is directly related to the number of valid times that can be outputted, which in the worst case is 12 * 60.

Therefore, the space complexity is O(1) — constant space complexity, since the maximum number of times that can be represented on a binary watch is fixed and does not grow with the size of the input.

Learn more about how to find time and space complexity quickly using problem constraints.

Fast Track Your Learning with Our Quick Skills Quiz:

Suppose k is a very large integer(2^64). Which of the following is the largest as n grows to infinity?


Recommended Readings


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫