3114. Latest Time You Can Obtain After Replacing Characters
Problem Description
You are given a string s
that represents a time in 12-hour format, where some digits might be replaced with question marks ("?"
).
The 12-hour time format follows the pattern "HH:MM"
where:
HH
represents hours and must be between00
and11
(inclusive)MM
represents minutes and must be between00
and59
(inclusive)- The earliest possible time is
00:00
and the latest is11:59
Your task is to replace all the "?"
characters in the string with valid digits to create a valid 12-hour time. Among all possible valid times you can create, you need to return the latest (maximum) possible time.
For example:
- If
s = "1?:?9"
, you could create times like"10:09"
,"11:19"
,"11:29"
, etc. The latest valid time would be"11:59"
. - If
s = "0?:5?"
, valid times include"00:50"
,"01:51"
,"09:59"
, etc. The latest would be"09:59"
.
The solution works by trying all possible times from the latest (11:59
) to the earliest (00:00
), checking if each time matches the pattern given in s
(where "?"
can match any digit). The first matching time found will be the latest possible time.
Intuition
Since we want to find the latest possible valid time, a natural approach is to start checking from the maximum possible time and work our way backwards. This greedy strategy ensures that the first valid time we find will automatically be the latest one.
Think of it like this: if we need to fill in the blanks to make the biggest number possible, we'd try to put the largest digits first. Here, instead of constructing the time digit by digit, we can leverage the fact that there are only a limited number of possible times in 12-hour format - just 12 × 60 = 720
total possibilities.
The key insight is that checking whether a time matches our pattern with "?"
wildcards is straightforward - we just need to verify that each non-"?"
character in the original string matches the corresponding digit in our candidate time. If they all match, then we can replace the "?"
characters with the corresponding digits from that time.
By iterating from 11:59
down to 00:00
, we guarantee that:
- We explore all possible valid times
- The first match we find is the maximum possible time
- We don't need complex logic to determine which digit to place where
This brute-force approach is efficient enough because the search space is small (only 720 possibilities), and the matching check for each candidate is O(1)
since the string length is fixed at 5 characters.
Solution Approach
The implementation uses a simple enumeration strategy with nested loops to check all possible times from latest to earliest.
Step-by-step breakdown:
-
Outer loop for hours: Iterate through hours from
11
down to0
usingrange(11, -1, -1)
. This ensures we check the latest hours first. -
Inner loop for minutes: For each hour, iterate through minutes from
59
down to0
usingrange(59, -1, -1)
. This ensures we check the latest minutes first for each hour. -
Format the time: For each combination of hour
h
and minutem
, create a formatted stringt
usingf"{h:02d}:{m:02d}"
. The:02d
ensures that single-digit numbers are padded with a leading zero (e.g.,9
becomes"09"
). -
Pattern matching: Check if the formatted time
t
matches the pattern ins
:- Use
zip(s, t)
to pair up corresponding characters from both strings - For each pair
(a, b)
, verify that eithera == "?"
(wildcard) ora == b
(exact match) - The expression
all(a == b for a, b in zip(s, t) if a != "?")
returnsTrue
only if all non-wildcard positions match
- Use
-
Return first match: As soon as we find a time that matches the pattern, return it immediately. Since we're checking from latest to earliest, this guaranteed to be the maximum valid time.
Example walkthrough with s = "1?:?9"
:
- Start checking from
11:59
: Compare with"1?:?9"
→"1" == "1"
✓,"1" matches "?"
✓,"5" matches "?"
✓,"9" == "9"
✓ → Match found! - Return
"11:59"
The algorithm has a time complexity of O(1)
since we check at most 720 fixed combinations, and space complexity of O(1)
as we only use a constant amount of extra space.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with s = "0?:5?"
:
Step 1: Start from the latest possible time (11:59) and work backwards
We'll check each time to see if it matches our pattern "0?:5?"
.
Step 2: Check 11:59
- Compare
"11:59"
with"0?:5?"
- Position 0:
"1"
vs"0"
→ No match (we need"0"
, not"1"
) - Skip this time
Step 3: Check 11:58
- Compare
"11:58"
with"0?:5?"
- Position 0:
"1"
vs"0"
→ No match - Skip this time
Step 4: Continue checking... (skipping hours 11 and 10)
All times starting with "11:"
or "10:"
will fail because they don't start with "0"
.
Step 5: Check 09:59
- Compare
"09:59"
with"0?:5?"
- Position 0:
"0"
vs"0"
→ Match! ✓ - Position 1:
"9"
vs"?"
→ Match! (wildcard) ✓ - Position 2:
":"
vs":"
→ Match! ✓ - Position 3:
"5"
vs"5"
→ Match! ✓ - Position 4:
"9"
vs"?"
→ Match! (wildcard) ✓
Step 6: Return the result
Since all positions match, "09:59"
is valid. Because we're checking from latest to earliest, this is the maximum possible time.
The algorithm replaces:
- The first
"?"
(position 1) with"9"
- The second
"?"
(position 4) with"9"
Final answer: "09:59"
Solution Implementation
1class Solution:
2 def findLatestTime(self, s: str) -> str:
3 # Iterate through all possible times from latest to earliest
4 # Hours: from 11 down to 00
5 for hour in range(11, -1, -1):
6 # Minutes: from 59 down to 00
7 for minute in range(59, -1, -1):
8 # Format the current time as HH:MM string
9 current_time = f"{hour:02d}:{minute:02d}"
10
11 # Check if current time matches the pattern
12 # Compare each character: if pattern has '?', any digit is valid
13 # If pattern has a specific digit, it must match
14 is_valid_match = all(
15 pattern_char == time_char
16 for pattern_char, time_char in zip(s, current_time)
17 if pattern_char != "?"
18 )
19
20 # Return the first valid time found (which is the latest due to iteration order)
21 if is_valid_match:
22 return current_time
23
1class Solution {
2 /**
3 * Finds the latest valid time that matches the given pattern.
4 * The input string contains time in "HH:MM" format where '?' can be any digit.
5 * Returns the maximum possible time that matches the pattern.
6 *
7 * @param s - time string with possible '?' wildcards
8 * @return the latest valid time matching the pattern
9 */
10 public String findLatestTime(String s) {
11 // Iterate through hours from 11 down to 0
12 // Using 11 as max since valid hours are 00-11 in 12-hour format
13 for (int hour = 11; hour >= 0; hour--) {
14 // For each hour, iterate through minutes from 59 down to 0
15 for (int minute = 59; minute >= 0; minute--) {
16 // Format the current time as "HH:MM"
17 String currentTime = String.format("%02d:%02d", hour, minute);
18
19 // Check if current time matches the pattern
20 boolean isValidMatch = true;
21
22 // Compare each character of the pattern with the current time
23 for (int index = 0; index < s.length(); index++) {
24 // If pattern has a non-wildcard character that doesn't match, it's invalid
25 if (s.charAt(index) != '?' && s.charAt(index) != currentTime.charAt(index)) {
26 isValidMatch = false;
27 break;
28 }
29 }
30
31 // If all characters match the pattern, return this time
32 // Since we're iterating from latest to earliest, first match is the answer
33 if (isValidMatch) {
34 return currentTime;
35 }
36 }
37 }
38
39 // This line should never be reached given valid input
40 // All patterns should have at least one valid time
41 return "";
42 }
43}
44
1class Solution {
2public:
3 string findLatestTime(string s) {
4 // Iterate through hours from 11 down to 0 (12-hour format)
5 for (int hour = 11; hour >= 0; hour--) {
6 // For each hour, iterate through minutes from 59 down to 0
7 for (int minute = 59; minute >= 0; minute--) {
8 // Format the current time as "HH:MM"
9 char timeString[6];
10 sprintf(timeString, "%02d:%02d", hour, minute);
11
12 // Check if the current time matches the pattern
13 bool isValid = true;
14 for (int i = 0; i < s.length(); i++) {
15 // If position is not '?' and doesn't match the generated time, it's invalid
16 if (s[i] != '?' && s[i] != timeString[i]) {
17 isValid = false;
18 break;
19 }
20 }
21
22 // If all positions match, return this time (it's the latest valid time)
23 if (isValid) {
24 return timeString;
25 }
26 }
27 }
28
29 // This line should never be reached given valid input
30 return "";
31 }
32};
33
1/**
2 * Finds the latest valid time that matches the given pattern string.
3 * The pattern string contains digits and '?' wildcards in the format "HH:MM".
4 * Returns the latest possible time (in 12-hour format, 00:00 to 11:59) that matches the pattern.
5 *
6 * @param s - The time pattern string with '?' as wildcards (e.g., "1?:?4")
7 * @returns The latest valid time matching the pattern in "HH:MM" format
8 */
9function findLatestTime(s: string): string {
10 // Start from the latest possible hour (11) and iterate backwards
11 for (let hour: number = 11; hour >= 0; hour--) {
12 // For each hour, check all minutes from 59 down to 0
13 for (let minute: number = 59; minute >= 0; minute--) {
14 // Format the current time as "HH:MM" with zero-padding
15 const currentTime: string = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
16
17 // Flag to track if current time matches the pattern
18 let isValidMatch: boolean = true;
19
20 // Compare each character of the pattern with the current time
21 for (let index: number = 0; index < s.length; index++) {
22 // If pattern has a specific digit (not '?'), it must match the current time
23 if (s[index] !== '?' && s[index] !== currentTime[index]) {
24 isValidMatch = false;
25 break;
26 }
27 }
28
29 // If all characters match, return this time as it's the latest valid one
30 if (isValidMatch) {
31 return currentTime;
32 }
33 }
34 }
35
36 // This point should never be reached if input is valid
37 return "";
38}
39
Time and Space Complexity
The time complexity is O(h × m)
, where h = 12
and m = 60
. The algorithm iterates through all possible hours from 11 down to 0 (12 iterations) and for each hour, iterates through all possible minutes from 59 down to 0 (60 iterations). In the worst case, when the match is found at "00:00" or no match exists, the algorithm checks all 12 × 60 = 720
possible time combinations. For each combination, the zip
and all
operations take O(5)
time (constant, as the time string has fixed length of 5 characters), which doesn't affect the overall complexity.
The space complexity is O(1)
. The algorithm only uses a constant amount of extra space regardless of input size. The variables h
, m
, and t
occupy fixed space, and the string t
is always 5 characters long. The zip
operation creates an iterator that doesn't store all pairs in memory at once, maintaining constant space usage.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Hour Range Assumption
A common mistake is assuming the 12-hour format uses hours from 01
to 12
instead of 00
to 11
. This misunderstanding comes from confusing the display format with the actual valid range in the problem.
Incorrect approach:
for hour in range(12, 0, -1): # Wrong: tries 12:xx which is invalid
for minute in range(59, -1, -1):
current_time = f"{hour:02d}:{minute:02d}"
# This would generate invalid times like "12:00"
Correct approach:
for hour in range(11, -1, -1): # Correct: uses 00-11 range
for minute in range(59, -1, -1):
current_time = f"{hour:02d}:{minute:02d}"
2. String Formatting Without Zero Padding
Forgetting to pad single-digit numbers with leading zeros will cause pattern matching failures.
Incorrect approach:
current_time = f"{hour}:{minute}" # Wrong: produces "1:5" instead of "01:05"
Correct approach:
current_time = f"{hour:02d}:{minute:02d}" # Correct: ensures "01:05" format
3. Wrong Iteration Direction
Starting from the earliest time (00:00) and returning the last match instead of starting from the latest time (11:59) and returning the first match. This makes the code unnecessarily complex and requires storing all matches.
Incorrect approach:
valid_times = []
for hour in range(12): # Wrong: starts from earliest
for minute in range(60):
current_time = f"{hour:02d}:{minute:02d}"
if is_match(s, current_time):
valid_times.append(current_time)
return valid_times[-1] if valid_times else "" # Needs extra storage
Correct approach:
for hour in range(11, -1, -1): # Correct: starts from latest
for minute in range(59, -1, -1):
current_time = f"{hour:02d}:{minute:02d}"
if is_match(s, current_time):
return current_time # Return immediately
4. Overly Complex Pattern Matching
Using regular expressions or complex string manipulation when simple character-by-character comparison suffices.
Overcomplicated approach:
import re pattern = s.replace("?", "\\d") if re.match(pattern, current_time): # Unnecessary regex overhead
Simple and correct approach:
is_valid_match = all(
pattern_char == time_char
for pattern_char, time_char in zip(s, current_time)
if pattern_char != "?"
)
Which of these pictures shows the visit order of a depth-first search?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!