I have struggled a lot with this Codility coding challenge: https://app.codility.com/programmers/challenges/chromium2017/
(The basic problem is: given a list of integers, count the number of ascending sequences possible where if the start of the sequence is at index, i
, no two neighbouring elements of the sequence (excluding the first two) are on the same side of i
. e.g., given [6, 2, 3, 4], starting at 2, we could go [2], [2, 3], [2, 4], [2, 6], [2, 3, 6] or [2, 4, 6].)
As of yet I am only capable of thinking of a solution with time complexity O(N^2) while O(N*log(N)) is required. All though somebody has posted a solution on GitHub, I don't have a clue what's going on:
https://github.com/kalwar/Codility/blob/master/chromimum2017_solution.c
It seems like he's doing affine transformations back and forth, but I lack the insight into why this works and why it can be achieved with O(N*Log(N)) time complexity. I was hoping somebody could shed a light.
I posted my own solution (in Java) below:
final class Chromium {
final long MODULUS = 1000000007L;
static class Nest implements Comparable<Nest> {
Nest(int index, int height) {
this.index = index;
this.height = height;
}
int index;
int height;
public int compareTo(Nest nest2) {
return Integer.compare(height, nest2.height);
}
}
/**
* Calculates the possibilities based on the fact that it is a multiplication of the runs of consecutive nests
* left and right of the nest in focus.
*/
private long getPossibleWays(Nest[] orderedNests, int startIndex) {
Nest startNest = orderedNests[startIndex];
long ways = 0;
long oppositeNumberOfWays = 0;
boolean previousLeft = false;
boolean first = true;
int runLength = 0;
for (int i = orderedNests.length - 1; i > startIndex; --i) {
Nest n = orderedNests[i];
boolean left = n.index < startNest.index;
if (left != previousLeft && !first) {
ways += (runLength * (oppositeNumberOfWays + 1)) % MODULUS;
long w = oppositeNumberOfWays;
oppositeNumberOfWays = ways;
ways = w;
runLength = 1;
} else {
runLength++;
}
first = false;
previousLeft = left;
}
ways += (runLength * (oppositeNumberOfWays + 1)) % MODULUS;
return 1 + ways + oppositeNumberOfWays;
}
public int solution(int[] H) {
Nest[] nests = new Nest[H.length];
for (int i = 0; i < H.length; ++i) {
nests[i] = new Nest(i, H[i]);
}
// Sort the nests by height
Arrays.sort(nests);
long possibleWays = 0;
for (int i = 0; i < nests.length; ++i) {
possibleWays += getPossibleWays(nests, i);
possibleWays = possibleWays % MODULUS;
}
return (int) possibleWays;
}
}