Given an array A
of N
integers we draw N
discs in a 2D plane, such that i-th disc has center in (0,i)
and a radius
O(N) complexity and O(N) memory solution.
private static int Intersections(int[] a)
{
int result = 0;
int[] dps = new int[a.length];
int[] dpe = new int[a.length];
for (int i = 0, t = a.length - 1; i < a.length; i++)
{
int s = i > a[i]? i - a[i]: 0;
int e = t - i > a[i]? i + a[i]: t;
dps[s]++;
dpe[e]++;
}
int t = 0;
for (int i = 0; i < a.length; i++)
{
if (dps[i] > 0)
{
result += t * dps[i];
result += dps[i] * (dps[i] - 1) / 2;
if (10000000 < result) return -1;
t += dps[i];
}
t -= dpe[i];
}
return result;
}
A 100/100 C# implementation as described by Aryabhatta (the binary search solution).
using System;
class Solution {
public int solution(int[] A)
{
return IntersectingDiscs.Execute(A);
}
}
class IntersectingDiscs
{
public static int Execute(int[] data)
{
int counter = 0;
var intervals = Interval.GetIntervals(data);
Array.Sort(intervals); // sort by Left value
for (int i = 0; i < intervals.Length; i++)
{
counter += GetCoverage(intervals, i);
if(counter > 10000000)
{
return -1;
}
}
return counter;
}
private static int GetCoverage(Interval[] intervals, int i)
{
var currentInterval = intervals[i];
// search for an interval starting at currentInterval.Right
int j = Array.BinarySearch(intervals, new Interval { Left = currentInterval.Right });
if(j < 0)
{
// item not found
j = ~j; // bitwise complement (see Array.BinarySearch documentation)
// now j == index of the next item larger than the searched one
j = j - 1; // set index to the previous element
}
while(j + 1 < intervals.Length && intervals[j].Left == intervals[j + 1].Left)
{
j++; // get the rightmost interval starting from currentInterval.Righ
}
return j - i; // reduce already processed intervals (the left side from currentInterval)
}
}
class Interval : IComparable
{
public long Left { get; set; }
public long Right { get; set; }
// Implementation of IComparable interface
// which is used by Array.Sort().
public int CompareTo(object obj)
{
// elements will be sorted by Left value
var another = obj as Interval;
if (this.Left < another.Left)
{
return -1;
}
if (this.Left > another.Left)
{
return 1;
}
return 0;
}
/// <summary>
/// Transform array items into Intervals (eg. {1, 2, 4} -> {[-1,1], [-1,3], [-2,6]}).
/// </summary>
public static Interval[] GetIntervals(int[] data)
{
var intervals = new Interval[data.Length];
for (int i = 0; i < data.Length; i++)
{
// use long to avoid data overflow (eg. int.MaxValue + 1)
long radius = data[i];
intervals[i] = new Interval
{
Left = i - radius,
Right = i + radius
};
}
return intervals;
}
}
A ruby solution. Scores 100%.
def solution(a)
# write your code in Ruby 2.2
open = Hash.new
close = Hash.new
(0..(a.length-1)).each do |c|
r = a[c]
open[ c-r ] ? open[ c-r ]+=1 : open[ c-r ]=1
close[ c+r ] ? close[ c+r ]+=1 : close[ c+r ]=1
end
open_now = 0
intersections = 0
open.merge(close).keys.sort.each do |v|
intersections += (open[v]||0)*open_now
open_now += (open[v]||0) - (close[v]||0)
if(open[v]||0)>1
# sum the intersections of only newly open discs
intersections += (open[v]*(open[v]-1))/2
return -1 if intersections > 10000000
end
end
intersections
end
Java 2*100%.
result
is declared as long for a case codility doesn't test, namely 50k*50k intersections at one point.
class Solution {
public int solution(int[] A) {
int[] westEnding = new int[A.length];
int[] eastEnding = new int[A.length];
for (int i=0; i<A.length; i++) {
if (i-A[i]>=0) eastEnding[i-A[i]]++; else eastEnding[0]++;
if ((long)i+A[i]<A.length) westEnding[i+A[i]]++; else westEnding[A.length-1]++;
}
long result = 0; //long to contain the case of 50k*50k. codility doesn't test for this.
int wests = 0;
int easts = 0;
for (int i=0; i<A.length; i++) {
int balance = easts*wests; //these are calculated elsewhere
wests++;
easts+=eastEnding[i];
result += (long) easts*wests - balance - 1; // 1 stands for the self-intersection
if (result>10000000) return -1;
easts--;
wests-= westEnding[i];
}
return (int) result;
}
}
My answer in Swift; gets a 100% score.
import Glibc
struct Interval {
let start: Int
let end: Int
}
func bisectRight(intervals: [Interval], end: Int) -> Int {
var pos = -1
var startpos = 0
var endpos = intervals.count - 1
if intervals.count == 1 {
if intervals[0].start < end {
return 1
} else {
return 0
}
}
while true {
let currentLength = endpos - startpos
if currentLength == 1 {
pos = startpos
pos += 1
if intervals[pos].start <= end {
pos += 1
}
break
} else {
let middle = Int(ceil( Double((endpos - startpos)) / 2.0 ))
let middlepos = startpos + middle
if intervals[middlepos].start <= end {
startpos = middlepos
} else {
endpos = middlepos
}
}
}
return pos
}
public func solution(inout A: [Int]) -> Int {
let N = A.count
var nIntersections = 0
// Create array of intervals
var unsortedIntervals: [Interval] = []
for i in 0 ..< N {
let interval = Interval(start: i-A[i], end: i+A[i])
unsortedIntervals.append(interval)
}
// Sort array
let intervals = unsortedIntervals.sort {
$0.start < $1.start
}
for i in 0 ..< intervals.count {
let end = intervals[i].end
var count = bisectRight(intervals, end: end)
count -= (i + 1)
nIntersections += count
if nIntersections > Int(10E6) {
return -1
}
}
return nIntersections
}
I got 100 out of 100 with this C++ implementation:
#include <map>
#include <algorithm>
inline bool mySortFunction(pair<int,int> p1, pair<int,int> p2)
{
return ( p1.first < p2.first );
}
int number_of_disc_intersections ( const vector<int> &A ) {
int i, size = A.size();
if ( size <= 1 ) return 0;
// Compute lower boundary of all discs and sort them in ascending order
vector< pair<int,int> > lowBounds(size);
for(i=0; i<size; i++) lowBounds[i] = pair<int,int>(i-A[i],i+A[i]);
sort(lowBounds.begin(), lowBounds.end(), mySortFunction);
// Browse discs
int nbIntersect = 0;
for(i=0; i<size; i++)
{
int curBound = lowBounds[i].second;
for(int j=i+1; j<size && lowBounds[j].first<=curBound; j++)
{
nbIntersect++;
// Maximal number of intersections
if ( nbIntersect > 10000000 ) return -1;
}
}
return nbIntersect;
}