See the original problem on HackerRank.
Solutions
This problem seems convoluted but it can be solved very easily if decomposed in terms of simple predicates:
- if the year is
1918
, we can directly return the day 26.09.1918
- otherwise we can check if the year is leap. In this case we return
12.09
and the year
- if the year is not leap, we just return
13.09
and the year
To determine if a year is leap, we have to apply either the Julian - if the year is less than 1918 - or the Gregorian rule - otherwise.
Here is an implementation in C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
bool isLeapGregorian(int year)
{
return year%400 == 0 ||
(year%4==0 && year%100!=0);
}
bool isLeapJulian(int year)
{
return year%4==0;
}
bool isLeap(int year)
{
return year < 1918 ? isLeapJulian(year) : isLeapGregorian(year);
}
string dayOfProgrammer(int year)
{
if (year == 1918)
return "26.09.1918";
if (isLeap(year))
return string("12.09.") + to_string(year);
return string("13.09.") + to_string(year);
}
int main()
{
int year; cin >> year;
cout << dayOfProgrammer(year);
}
|
An alternative solution consists in calculating the number of days in February:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// ...other functions are the same as before...
bool is1918(int year)
{
return year == 1918;
}
int main()
{
int y; cin >> y;
auto daysInFebruary = is1918(y) ? (28-13) : (isLeap(y) ? 29 : 28);
int day = 256 - 31 - daysInFebruary - 31 - 30 - 31 - 30 - 31 - 31;
printf("%02d.%02d.%04d", day, 9, y);
}
|
A step forward with this exercise is making the solution a bit more flexible. What if we want to add more rules?
An idea: we put the rules into a vector and we try them in order. The first successful rule just breaks the loop.
Here is a possible implementation using optional
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
optional<int> asDefault([[maybe_unused]]int year)
{
return 28;
}
optional<int> asLeap(int year)
{
return isLeap(year) ? optional{29} : nullopt;
}
optional<int> as1918(int year)
{
return year == 1918 ? optional{28-13} : nullopt;
}
int main()
{
int year = 2016;
vector rules = {as1918, asLeap, asDefault};
auto daysInFebruary = 0;
for (auto rule : rules)
{
if (auto res = rule(year); res)
{
daysInFebruary = *res;
break;
}
}
int day = 256 - 31 - daysInFebruary - 31 - 30 - 31 - 30 - 31 - 31;
printf("%02d.%02d.%04d", day, 9, year);
}
|
You can split asLeap
. For example:
1
2
3
4
5
6
7
8
9
|
optional<int> asLeapJulian(int year)
{
return (year < 1918 && year%4 == 0) ? optional{29} : nullopt;
}
optional<int> asLeapGregorian(int year)
{
return (year > 1918 && (year%400 == 0 || (year%4==0 && year%100!=0))) ? optional{29} : nullopt;
}
|
The rules are just pointer to functions. Note that thanks to automatic deduction of class templates (since C++17) we can omit tedious details about the type of vector.