LeetCode 1904 - The Number of Full Rounds You Have Played
The problem describes an online chess tournament where a new round begins every 15 minutes. A player can only be credited for a full round if they are present for the entire duration of that round.
Difficulty: 🟡 Medium
Topics: Math, String
Solution
Problem Understanding
The problem describes an online chess tournament where a new round begins every 15 minutes. A player can only be credited for a full round if they are present for the entire duration of that round.
The input consists of two time strings:
loginTime, the moment the player logs inlogoutTime, the moment the player logs out
Both are given in "hh:mm" 24-hour format.
The task is to compute how many complete 15-minute rounds the player fully participated in.
A round is considered full only if:
- the player logged in before or exactly when the round started
- the player logged out after or exactly when the round ended
For example, if a round runs from 09:45 to 10:00, then:
- logging in at
09:44is acceptable - logging in at
09:46is not - logging out at
10:00is acceptable - logging out at
09:59is not
The tricky part of the problem is handling overnight play. If logoutTime is earlier than loginTime, that means the session crossed midnight.
For example:
- login:
21:30 - logout:
03:00
means the player stayed connected from 21:30 to 24:00, then continued from 00:00 to 03:00.
The constraints are very small because there are only 24 hours in a day, but the problem is fundamentally about careful time arithmetic and interval alignment. The main challenge is correctly rounding the login time upward to the next 15-minute boundary and rounding the logout time downward to the previous 15-minute boundary.
Important edge cases include:
- login and logout spanning midnight
- login exactly on a 15-minute boundary
- logout exactly on a 15-minute boundary
- intervals shorter than 15 minutes
- sessions that contain zero full rounds
- sessions that almost complete a round but miss by one minute
The problem guarantees that loginTime != logoutTime, so the session duration is never exactly 24 hours.
Approaches
Brute Force Approach
A straightforward solution is to simulate every possible round in the day.
There are only 96 rounds total because:
$$24 \times 4 = 96$$
Each round lasts 15 minutes. We could generate every round interval and check whether the player's session fully covers that interval.
To do this:
- Convert login and logout times into minutes.
- Handle overnight sessions by extending the logout time by 1440 minutes if needed.
- Iterate through every possible round start time.
- Check whether the player fully covers that round.
This works because every valid full round is explicitly verified.
However, this approach does unnecessary work. We do not actually need to inspect every round individually because the rounds are perfectly aligned on fixed 15-minute boundaries.
Optimal Approach
The key insight is that only aligned 15-minute boundaries matter.
Instead of checking every round, we can directly compute:
- the earliest round start the player could fully participate in
- the latest round end the player could fully participate in
We achieve this by:
- rounding the login time upward to the next multiple of 15
- rounding the logout time downward to the previous multiple of 15
The number of complete rounds is then simply:
$$\frac{\text{adjustedLogout} - \text{adjustedLogin}}{15}$$
If the session crosses midnight, we add 1440 minutes to the logout time before performing the calculation.
This transforms the problem into simple arithmetic.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(96) | O(1) | Checks every possible round explicitly |
| Optimal | O(1) | O(1) | Uses arithmetic and boundary alignment |
Algorithm Walkthrough
- Convert both times into total minutes from midnight.
For example:
"09:31"becomes571"10:14"becomes614
This simplifies all later calculations because working with integers is easier than manipulating strings. 2. Handle overnight sessions.
If logoutMinutes < loginMinutes, the session crossed midnight. In that case, add 1440 minutes to the logout time.
For example:
- login:
21:30→1290 - logout:
03:00→180
Since 180 < 1290, convert logout to:
$$180 + 1440 = 1620$$ 3. Round the login time upward to the next 15-minute boundary.
A player can only participate in rounds that start after they are logged in.
The formula is:
$$\left(\frac{login + 14}{15}\right) \times 15$$
Integer division effectively performs a ceiling operation. 4. Round the logout time downward to the previous 15-minute boundary.
A player must remain connected until the round ends.
The formula is:
$$\left(\frac{logout}{15}\right) \times 15$$ 5. Compute the difference between the adjusted times.
If the adjusted logout is earlier than the adjusted login, there are zero complete rounds.
Otherwise:
$$\frac{adjustedLogout - adjustedLogin}{15}$$ 6. Return the computed value.
Why it works
Every valid round begins on a multiple of 15 minutes. By rounding the login time upward, we guarantee the player is present before the round starts. By rounding the logout time downward, we guarantee the player stays connected until the round ends.
Every 15-minute interval between these two adjusted boundaries corresponds exactly to one complete round. Therefore, counting these intervals produces the correct answer.
Python Solution
class Solution:
def numberOfRounds(self, loginTime: str, logoutTime: str) -> int:
def to_minutes(time_str: str) -> int:
hours, minutes = map(int, time_str.split(":"))
return hours * 60 + minutes
login_minutes = to_minutes(loginTime)
logout_minutes = to_minutes(logoutTime)
# Handle overnight session
if logout_minutes < login_minutes:
logout_minutes += 24 * 60
# Round login up to next 15-minute boundary
adjusted_login = ((login_minutes + 14) // 15) * 15
# Round logout down to previous 15-minute boundary
adjusted_logout = (logout_minutes // 15) * 15
# No valid full rounds
if adjusted_logout < adjusted_login:
return 0
return (adjusted_logout - adjusted_login) // 15
The implementation begins with a helper function that converts "hh:mm" into total minutes from midnight. This standardizes all calculations into integer arithmetic.
Next, the code checks whether the session crossed midnight. If so, it extends the logout time by one full day, allowing the interval to remain continuous.
The login time is rounded upward because partial participation at the beginning of a round does not count. The logout time is rounded downward because partial participation at the end also does not count.
Finally, the number of full rounds is computed by dividing the aligned interval length by 15.
Go Solution
package main
import (
"strconv"
"strings"
)
func numberOfRounds(loginTime string, logoutTime string) int {
toMinutes := func(timeStr string) int {
parts := strings.Split(timeStr, ":")
hours, _ := strconv.Atoi(parts[0])
minutes, _ := strconv.Atoi(parts[1])
return hours*60 + minutes
}
loginMinutes := toMinutes(loginTime)
logoutMinutes := toMinutes(logoutTime)
// Handle overnight session
if logoutMinutes < loginMinutes {
logoutMinutes += 24 * 60
}
// Round login up
adjustedLogin := ((loginMinutes + 14) / 15) * 15
// Round logout down
adjustedLogout := (logoutMinutes / 15) * 15
if adjustedLogout < adjustedLogin {
return 0
}
return (adjustedLogout - adjustedLogin) / 15
}
The Go implementation closely mirrors the Python version. Time parsing uses strings.Split and strconv.Atoi.
Because Go integer division automatically truncates toward zero, the rounding formulas work naturally without additional helper functions.
There are no concerns about integer overflow because the largest possible value is only slightly above 1440 minutes.
Worked Examples
Example 1
Input:
loginTime = "09:31"
logoutTime = "10:14"
Convert times to minutes:
| Value | Minutes |
|---|---|
| Login | 571 |
| Logout | 614 |
Round boundaries:
| Step | Calculation | Result |
|---|---|---|
| Adjust login upward | ((571 + 14) // 15) * 15 | 585 |
| Adjust logout downward | (614 // 15) * 15 | 600 |
These correspond to:
| Minutes | Time |
|---|---|
| 585 | 09:45 |
| 600 | 10:00 |
Compute rounds:
$$\frac{600 - 585}{15} = 1$$
Answer:
1
Example 2
Input:
loginTime = "21:30"
logoutTime = "03:00"
Convert times:
| Value | Minutes |
|---|---|
| Login | 1290 |
| Logout | 180 |
Since logout is earlier, add one day:
| Step | Result |
|---|---|
| Adjusted logout | 1620 |
Round boundaries:
| Step | Calculation | Result |
|---|---|---|
| Adjust login upward | ((1290 + 14) // 15) * 15 | 1290 |
| Adjust logout downward | (1620 // 15) * 15 | 1620 |
Compute rounds:
$$\frac{1620 - 1290}{15} = 22$$
Answer:
22
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(1) | Only a fixed number of arithmetic operations are performed |
| Space | O(1) | No extra data structures proportional to input size are used |
The algorithm performs only constant-time operations: parsing strings, arithmetic rounding, and one final division. Since the input size is fixed and independent of any variable n, both time and space complexity remain constant.
Test Cases
sol = Solution()
assert sol.numberOfRounds("09:31", "10:14") == 1 # example 1
assert sol.numberOfRounds("21:30", "03:00") == 22 # overnight example
assert sol.numberOfRounds("00:00", "00:15") == 1 # exactly one full round
assert sol.numberOfRounds("00:01", "00:14") == 0 # no full round
assert sol.numberOfRounds("12:00", "12:44") == 2 # two complete rounds
assert sol.numberOfRounds("12:00", "12:45") == 3 # exact boundary ending
assert sol.numberOfRounds("12:01", "12:45") == 2 # rounded login
assert sol.numberOfRounds("23:46", "00:14") == 0 # overnight but insufficient time
assert sol.numberOfRounds("23:45", "00:00") == 1 # overnight exact round
assert sol.numberOfRounds("10:15", "10:15") == 0 # hypothetical equal times
assert sol.numberOfRounds("05:00", "05:59") == 3 # nearly four rounds
assert sol.numberOfRounds("18:14", "18:16") == 0 # tiny interval
assert sol.numberOfRounds("18:00", "18:59") == 3 # multiple rounds
| Test | Why |
|---|---|
"09:31" -> "10:14" |
Basic partial-round example |
"21:30" -> "03:00" |
Overnight session |
"00:00" -> "00:15" |
Single exact round |
"00:01" -> "00:14" |
No full rounds |
"12:00" -> "12:44" |
Multiple valid rounds |
"12:00" -> "12:45" |
Exact logout boundary |
"12:01" -> "12:45" |
Login requires rounding upward |
"23:46" -> "00:14" |
Overnight but too short |
"23:45" -> "00:00" |
Overnight exact interval |
"10:15" -> "10:15" |
Degenerate equal-time scenario |
"05:00" -> "05:59" |
Near-complete hour |
"18:14" -> "18:16" |
Very short interval |
"18:00" -> "18:59" |
Several complete rounds |
Edge Cases
One important edge case occurs when the session crosses midnight. A naive implementation might assume that logout always happens later on the same day, causing negative durations or incorrect round counts. The implementation handles this by adding 1440 minutes to the logout time whenever it is numerically smaller than the login time.
Another tricky case happens when the login time is not aligned to a 15-minute boundary. For example, logging in at 09:31 means the player missed the round that began at 09:30. The implementation correctly rounds upward to 09:45, ensuring only fully covered rounds are counted.
A third important edge case involves logout times that occur just before a round ends. For instance, logging out at 10:14 should not count the 10:00 to 10:15 round. By rounding the logout time downward to the previous 15-minute boundary, the implementation guarantees incomplete ending rounds are excluded.
Another subtle case is intervals shorter than 15 minutes. Even though the player was connected, there may not be enough time to fully participate in any round. The implementation naturally handles this because the adjusted logout time becomes less than the adjusted login time, producing zero rounds.