See the original problem on HackerRank.
Solutions
A simple solution consists in breaking the total uptime in seconds into progressively larger time units using integer division and modulo operations. First, it computes how many full days fit into the given seconds, then uses the remaining seconds to calculate hours, minutes, and finally seconds. This approach runs in constant time:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
def format_uptime(seconds: int) -> str:
days = seconds // 86400
seconds %= 86400
hours = seconds // 3600
seconds %= 3600
minutes = seconds // 60
seconds %= 60
return f"{days}d {hours}h {minutes}m {seconds}s"
seconds = int(input())
print(format_uptime(seconds))
|
Another more extensible approach keeps units in a dedicated array, avoiding duplicated logic and arithmetic. The formatting is easily configurable for different needs or policies, also can be passed from outside if needed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
unsigned elapsed; cin >> elapsed;
static constexpr std::array<std::pair<int, std::string_view>, 4> units{ {
{24*3600, "d "},
{3600, "h "},
{60, "m "},
{1, "s"},
} };
std::string uptime;
for (const auto& [ratio, unit] : units)
{
uptime += std::to_string(elapsed / ratio).append(unit);
elapsed %= ratio;
}
std::cout << uptime << "\n";
|
This code can be easily adapted for satisfying the bonus request (do not print 0 units):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
unsigned elapsed; cin >> elapsed;
static constexpr std::array<std::pair<int, std::string_view>, 4> units{ {
{24*3600, "d "},
{3600, "h "},
{60, "m "},
{1, "s"},
} };
std::string uptime;
for (const auto& [ratio, unit] : units)
{
const auto ratioElapsed = elapsed / ratio;
if (ratioElapsed)
{
uptime += std::to_string(ratioElapsed).append(unit);
}
elapsed %= ratio;
}
std::cout << uptime << "\n";
|
Note that there is no need to handle the all-zero case explicitly, because the problem constraints guarantee that the minimum elapsed time is 1 second.
Just for fun, it’s interesting to see the patterns involved in this problem are a combination of prefix sum, zip and reduce. This is an example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
static constexpr std::array<std::pair<unsigned, std::string_view>, 4> units{ {
{24*3600, "d "},
{3600, "h "},
{60, "m "},
{1, "s"},
} };
std::array<unsigned, units.size()> mods{};
exclusive_scan(begin(units), end(units), begin(mods), elapsed, [](auto sum, auto i){
return sum % i.first;
});
auto zipped = views::zip(mods, units);
cout << accumulate(begin(zipped), end(zipped), string{}, [](auto sum, const auto& p){
return std::move(sum) + std::to_string(get<0>(p) / get<1>(p).first).append(get<1>(p).second);
});
|
If C++ ranges provided exclusive_scan and accumulate, this code could be rewritten as a fully lazy snippet (something can be achieved with range-v3 that provides implementations of partial_sum and accumulate).