Introduction
We can look at the Day 5 problem as on the binary definition of
seats numbers with predefined letters instead of 0
s and 1
s.
Solution
By going through given seat definition as an iterable of characters, we can divide our predefined space range
into halves and that’s what we start with by using String.findPlace(): Int?
. The function returns null
if
is impossible to define the place - the range of matching places has more than single element. It uses a helper
function String.select
which is its actual implementation - it goes through the characters of given String
and selects the proper part of range, based on its arguments low
and high
.
|
|
The first part is as easy as finding the biggest number of seat, which can easily by done with Kotlin extension function
fun <T : Comparable<T>> Iterable<T>.maxOrNull(): T?
.
In the second part we need to find the gap in the seats numbering. In such problems it’s usually a good idea to sort the items that we are processing, as this costs only $O(n \log n)$ time, so it’s not so much compared to $O(n)$ which is required for input data processing. Having the seats sorted, finding a gap is as easy as finding a 3 elements window slice for which elements $(x, y, z)$ it’s not true that $x + 1 = y$ and $y + 1 = z$.
Extra code comments
There are two things in the task solution that are worth mentioning:
- We should remember of using the sequences when making multiple operations on iterables in Kotlin. In this example it
isn’t crucial but let’s notice that this can be easily achieved with single call of extension function
fun <T> Iterable<T>.asSequence(): Sequence<T>
as most of the functions available for collections are also available for sequences. - It’s worth mentioning how the
fold
function works and why it’s used here. When we think about processing some data in a loop by iterating over it and holding accumulated value during that process,fold
is usually the best way to express our intention. It can be simply defined forIterable<T>
as we find it in standard librarywhich originally it’s defined as1 2 3 4 5
fun <T, R> Iterable<T>.fold(init: R, process: (acc: R, T) -> R): R { var acc = init for (element in this) acc = process(acc, element) return acc }
inline
function, so this approach doesn’t bring extra cost but makes our code more readable.