Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

10.23 Largest Time For Given Digits

Source: src/main/kotlin/microsoft/ValidTime.kt Pattern: permutation search with validity filter · Core page

The Problem

The largest valid HH:MM from four digits (each used once), or “”.

  • Constraints: 4 digits 0-9.

Examples

Input:  [1,2,3,4]   -> Output: "23:41"
Input:  [5,5,5,5]   -> Output: ""

Intuition — try all permutations; keep the max valid time

The repo’s ValidTime counts valid permutations; the classic problem wants the largest — same permutation machinery, different reducer:

var best = ""
val seen = BooleanArray(4)

fun backtrack(current: String) {
    if (current.length == 4) {
        val hours = current.substring(0, 2).toInt()
        val minutes = current.substring(2, 4).toInt()
        if (hours in 0..23 && minutes in 0..59 && current > best) best = current
        return
    }
    for (i in digits.indices) {
        if (!seen[i]) { seen[i] = true; backtrack(current + digits[i]); seen[i] = false }
    }
}

Why string comparison for “largest”? HHMM as a 4-digit string — lexicographic order on equal-length strings IS numeric order. The 12.0 permutation engine with a validity gate.

Approach 1 — Generate all 4! permutations, filter, max (the repo family, optimal)

class LargestTimeForGivenDigits {
    private val digits = intArrayOf(0, 0, 0, 0)
    private val used = BooleanArray(4)
    private var best = ""

    /**
     * @param arr four digits
     * @return    largest valid HH:MM or ""
     */
    fun largestTimeFromDigits(arr: IntArray): String {
        for (i in 0 until 4) digits[i] = arr[i]
        used.fill(false)
        best = ""
        backtrack("")
        return best
    }

    private fun backtrack(current: String) {
        if (current.length == 4) {
            val hours = current.substring(0, 2).toInt()
            val minutes = current.substring(2, 4).toInt()

            if (hours in 0..23 && minutes in 0..59 && current > best) {
                best = current
            }
            return
        }

        for (i in 0 until 4) {
            if (!used[i]) {
                used[i] = true
                backtrack(current + digits[i])
                used[i] = false
            }
        }
    }
}
public class LargestTimeForGivenDigits {
    private int[] digits = new int[4];
    private boolean[] used = new boolean[4];
    private String best = "";

    private void backtrack(String cur) {
        if (cur.length() == 4) {
            int h = Integer.parseInt(cur.substring(0, 2));
            int m = Integer.parseInt(cur.substring(2, 4));

            if (h <= 23 && m <= 59 && cur.compareTo(best) > 0) best = cur;
            return;
        }

        for (int i = 0; i < 4; i++) {
            if (!used[i]) {
                used[i] = true;
                backtrack(cur + digits[i]);
                used[i] = false;
            }
        }
    }

    /**
     * @param arr four digits
     * @return    largest valid HH:MM or ""
     */
    public String largestTimeFromDigits(int[] arr) {
        digits = arr.clone();
        used = new boolean[4];
        best = "";
        backtrack("");
        return best;
    }
}
#include <string>
#include <vector>

class LargestTimeForGivenDigits {
    std::vector<int> digits;
    std::vector<bool> used;
    std::string best;

    void backtrack(std::string cur) {
        if (cur.size() == 4) {
            int h = std::stoi(cur.substr(0, 2));
            int m = std::stoi(cur.substr(2, 2));

            if (h <= 23 && m <= 59 && cur > best) best = cur;
            return;
        }

        for (int i = 0; i < 4; i++) {
            if (!used[i]) {
                used[i] = true;
                backtrack(cur + std::to_string(digits[i]));
                used[i] = false;
            }
        }
    }

public:
    /**
     * @param arr four digits
     * @return    largest valid HH:MM or ""
     */
    std::string largestTimeFromDigits(std::vector<int>& arr) {
        digits = arr;
        used.assign(4, false);
        best = "";
        backtrack("");
        return best;
    }
};
def largest_time_from_digits(arr: list[int]) -> str:
    """
    @param arr: four digits
    @return:    largest valid HH:MM or ""
    """
    best = ""
    used = [False] * 4

    def backtrack(cur: str) -> None:
        nonlocal best
        if len(cur) == 4:
            h, m = int(cur[:2]), int(cur[2:])
            if h <= 23 and m <= 59 and cur > best:
                best = cur
            return

        for i in range(4):
            if not used[i]:
                used[i] = True
                backtrack(cur + str(arr[i]))
                used[i] = False

    backtrack("")
    return best
#![allow(unused)]
fn main() {
impl Solution {
    /// @param arr four digits
    /// @return    largest valid HH:MM or ""
    pub fn largest_time_from_digits(arr: Vec<i32>) -> String {
        let mut best = String::new();
        let mut used = vec![false; 4];

        fn backtrack(arr: &Vec<i32>, used: &mut Vec<bool>, cur: &mut String, best: &mut String) {
            if cur.len() == 4 {
                let h: i32 = cur[0..2].parse().unwrap();
                let m: i32 = cur[2..4].parse().unwrap();

                if h <= 23 && m <= 59 && cur > best {
                    *best = cur.clone();
                }
                return;
            }

            for i in 0..4 {
                if !used[i] {
                    used[i] = true;
                    let d = arr[i].to_string();
                    cur.push_str(&d);
                    backtrack(arr, used, cur, best);
                    cur.truncate(cur.len() - 1);
                    used[i] = false;
                }
            }
        }

        backtrack(&arr, &mut used, &mut String::new(), &mut best);
        best
    }
}
}

Dry run

Input: arr = [1,2,3,4].

permutations: "1234" (12:34 valid, best), "1243" (12:43 valid, better), "1324" (13:24),
"1342" (13:42), "1423" (14:23), "1432" (14:32), "2134" (21:34), "2143" (21:43),
"2314" (23:14), "2341" (23:41 valid — best!), ...  "2413" (24:13 invalid: 24 hours).

Output: "23:41" ✓

The filter h <= 23 && m <= 59 prunes the invalid permutations; the string > keeps the lexicographically largest — which for fixed-length strings is the numerically largest time. [5,5,5,5]: every permutation is “55:55” — invalid → best stays “” ✓.

Complexity

Time. 4! permutations:

$$ T = O(4!) = O(24) = O(1) $$

Space. The backtrack:

$$ S = O(4) = O(1) $$

Variants & follow-ups

  • Restore IP Addresses (12.6) — the same permutation/filter pattern with segment rules.
  • Interview follow-up: “Why not greedy per digit?” The hour constraint (0-23) is positional — the largest possible first digit (2) forces constraints on the second (0-3). Greedy fails on [2,0,6,6] (26:xx invalid → 20:66 invalid → the answer is actually 06:26… no wait, 20:66 invalid, the answer is “”). The brute-force permutation handles the coupling exactly.