What's wrong with this solution for Max Counters codility challenge
Question:
So I’ve been going through the tests on codility and got bit stuck with the “Max Counters” one (link https://codility.com/demo/take-sample-test/max_counters). My first, and obvious solution was the following one:
def solution(N, A):
counters = N * [0];
for a in A:
if 1 <= a <= N:
counters[a - 1] += 1;
elif a == N + 1:
counters = N * [max(counters)];
return counters
which works just fine, but takes too much time, due to the fact that each call to max counters fills an entire array.
So I came up with the following solution which seems to work ok for small inputs, but randomly provides incorrect results for medium and large ones.
def solution(N, A):
counters = N * [0];
current_max = 0;
last_update = 0;
for a in A:
if 1 <= a <= N:
counters[a - 1] += 1;
if counters[a - 1] < last_update:
counters[a - 1] = last_update + 1;
if counters[a - 1] > current_max:
current_max = counters[a - 1];
elif a == N + 1:
last_update = current_max;
for i in xrange(len(counters)):
if counters[i] < last_update:
counters[i] = last_update;
return counters
I can’t seem to figure out what’s wrong with it.
Edit: Result – http://codility.com/demo/results/demoQA7BVQ-NQT/
Answers:
One problem is here:
counters[a - 1] += 1
if counters[a - 1] < last_update:
counters[a - 1] = last_update + 1
what if counters[a - 1]
was last_update - 1
?
You can take a look at my solution(written in C# though):
public static int[] solution(int N, int[] A)
{
// write your code in C# with .NET 2.0
var counters = new int[N];
var defaultValueToInitialize = 0;
var maxElement = 0;
//initializing the counters values, without increasing the N+1 actions
foreach (var num in A)
{
if (num == N + 1)
{
defaultValueToInitialize = maxElement;
counters = new int[N];
}
else
{
counters[num - 1]++;
if (counters[num - 1] + defaultValueToInitialize > maxElement)
maxElement = counters[num - 1] + defaultValueToInitialize;
}
}
//adding the increased default value to each cell
for (int i = 0; i < counters.Length; i++)
{
counters[i] += defaultValueToInitialize;
}
return counters;
}
Check this one (python, get’s 100 score):
The secret is not to update all the counters every time you get the instruction to bump them all up to a new minimum value. This incurs an operation involving every counter on every occasion, and is the difference between a ~60% score and a 100% score.
Instead, avoid this hit by keeping track of the current minimum and maximum values; using and updating them for each counter you visit.
Then, after all the instructions are processed, because there may be counters which haven’t been touched with their own personal update since the last update-all instruction, pass over the counters themselves and ensure they are at the minimum value.
def solution(N, A):
res = [0] * N
max_val = 0
last_update = 0
n1 = N+1
for i in A:
if i < n1:
if res[i-1] < last_update:
res[i-1] = last_update
res[i-1]+=1
if res[i-1] > max_val:
max_val = res[i-1]
else:
last_update = max_val
for i in xrange(len(res)):
if res[i] < last_update:
res[i] = last_update
return res
Consider this 100/100 solution in Ruby:
# Algorithm:
#
# * Maintain a maximum value.
# * For each `increase(X)` command update respective counter.
# * For each `max_counter` command save the current max as `set_max` for later use.
# * Once the loop is over, make an adjustment pass to set all values less than `set_max` to `set_max`.
def solution(n, commands)
max = set_max = 0
counters = Array.new(n, 0)
commands.each do |cmd|
if cmd <= n
# This is an `increase(X)` command.
value = [counters[cmd - 1], set_max].max + 1
counters[cmd - 1] = value
max = [value, max].max
else
# This is a `max_counter` command.
# Just update `set_max`.
set_max = max
end
end
# Finalize -- set counters less than `set_max` to `set_max`.
counters.map! {|value| [value, set_max].max}
# Result.
counters
end
#--------------------------------------- Tests
def test
sets = []
sets << ["1", [1], 1, [1]]
sets << ["sample", [3, 2, 2, 4, 2], 5, [3, 4, 4, 6, 1, 4, 4]]
sets.each do |name, expected, n, commands|
out = solution(n, commands)
raise "FAILURE at test #{name.inspect}: #{out.inspect} != #{expected.inspect}" if out != expected
end
puts "SUCCESS: All tests passed"
end
My python version only scores 66 points as it’s a little too slow for the later tests.
def solution(N, A):
counters = [0 for x in range(N)]
for elem in A:
if elem > N:
cmax = max(counters)
counters = [cmax for x in range(N)]
else:
counters[elem-1] += 1
return counters
100/100 solution in C
struct Results
solution(int N, int A[], int M)
{
struct Results result;
int *cnts = calloc(N, sizeof(int));
int i = 0, maxcnt = 0, j = 0, lastcnt = 0;
for (i = 0; i < M; i++) {
if (A[i] <= N && A[i] >= 1) {
if (cnts[A[i] - 1] < lastcnt)
cnts[A[i] - 1] = lastcnt + 1;
else
cnts[A[i] - 1] += 1;
if (cnts[A[i] - 1] > maxcnt)
maxcnt = cnts[A[i] - 1];
}
if (A[i] == N + 1)
lastcnt = maxcnt;
}
for (j = 0; j < N; j++) {
if (cnts[j] < lastcnt)
cnts[j] = lastcnt;
}
result.C = cnts;
result.L = N;
return result;
}
MaxCounters solution in C
struct Results solution(int N, int A[], int M) {
struct Results result;
// write your code in C90
int i,k=0,max_v=0;
result.C =(int*)(malloc(N*sizeof(int)));
result.L = N;
memset(result.C, 0, N*sizeof(int));
for(i=0;i<M;i++)
{
if (A[i] > N)
max_v=k;
else
{
if(result.C[A[i]-1] < max_v)
result.C[A[i]-1]=max_v;
result.C[A[i]-1]+=1;
if(result.C[A[i]-1] > k)
k=result.C[A[i]-1];
}
}
for(i=0;i<N;i++)
{
if(result.C[i] < max_v)
result.C[i]=max_v;
}
return result;
}
Javascript 100/100
function solution(N, A) {
var j, len = A.length, lastBase = 0, max = 0,
counters = [], n1 = N+1;
for(j=0; j<N; j+=1){
counters[j]=0; //initArray
}
for(j=0; j < len; j+=1){
if(A[j]<n1){
if(counters[A[j]-1] < lastBase) {
counters[A[j]-1] = lastBase;
}
counters[A[j]-1] += 1;
if(max < counters[A[j]-1]) {
max = counters[A[j]-1];
}
} else {
lastBase = max;
}
}
for(j=0; j<N; j+=1){
if(counters[j] < lastBase) {
counters[j] = lastBase;
}
}
return counters;
}
This is a modified version of @jacoor’s solution with slightly more idiomatic python and variable names and if statement conditions more closely reflecting the problem description.
def fast_solution(N, A):
counters = [0] * N
max_counter = 0
last_update = 0
for K,X in enumerate(A): # O(M)
if 1 <= X <= N:
counters[X-1] = max(counters[X-1], last_update)
counters[X-1] += 1
max_counter = max(counters[X-1], max_counter)
elif A[K] == (N + 1):
last_update = max_counter
for i in xrange(N): # O(N)
counters[i] = max(counters[i], last_update)
return counters
Javascript 100/100
function solution(N, A) {
var max = 0,
offset = 0,
counters = Array.apply(null, Array(N)).map(function () {return 0;});
A.forEach(function (d) {
if (d === N + 1) {
offset = max;
}
else {
counters[d-1] = Math.max(offset + 1, counters[d-1] + 1);
max = Math.max(counters[d-1], max);
}
});
counters.map(function (d, i) {
if (d < offset) {
counters[i] = offset;
}
});
return counters;
}
Java solution:
public int[] solution(int N, int[] A) {
// write your code in Java SE 8
int[] counter = new int[N];
int maxCounter = 0;
int pos;
int last_max=0;
for (int i = 0; i < A.length; i++) {
if (A[i] <= N) {
pos = A[i];
if (counter[pos - 1] < last_max)
counter[pos - 1] = last_max;
counter[pos - 1] += 1;
if (maxCounter < counter[pos - 1])
maxCounter = counter[pos - 1];
}
else{
last_max=maxCounter;
}
}
for (int i = 0; i < counter.length; i++) {
if (counter[i] < last_max)
counter[i] = last_max;
}
return counter;
}
C++ 100/100
The key is to IGNORE the sample iteration in the problem, it will lead you to a O(m*n) time complexity solution.
vector<int> solution(int N, vector<int> &A) {
// write your code in C++11
vector<int> counters(N,0);
int last_reset = 0, max_count = 0;
for( unsigned int a=0; a < A.size(); ++a)
{
int current_int = A.at (a);
if (current_int == (N+1))
{
last_reset = max_count;
}
else
{
unsigned int counter_index = current_int - 1;
if ( counters.at (counter_index) < last_reset)
counters.at (counter_index) = last_reset + 1;
else
++counters.at (counter_index);
if ( counters.at (counter_index) > max_count)
max_count = counters.at (counter_index);
}
}
for( unsigned int n=0; n < counters.size(); ++n)
{
if ( counters.at (n) < last_reset)
counters.at (n) = last_reset;
}
return counters;
}
C# – a 100/100 solution
public int[] solution(int N, int[] A) {
// write your code in C# 6.0 with .NET 4.5 (Mono)
int[] counter = new int[N];
int maxValue = 0;
int minValue = 0;
for(int i=0;i<A.Length;i++)
{
//less than or equal to length N
if(A[i] <= N)
{
if(counter[A[i] - 1] < minValue)
{
counter[A[i] - 1] = minValue;
}
counter[A[i] - 1] += 1;
if(counter[A[i] - 1] > maxValue)
{
maxValue = counter[A[i] - 1];
}
}
else if(A[i] == N+1)
{
minValue = maxValue;
}
}
for(int j=0;j<counter.Length;j++)
{
if(counter[j] < minValue)
{
counter[j] = minValue;
}
}
return counter;
}
here is the shortest solution in Python:
def solution(N, A):
out = [0 for _ in range(N)]
for ele in A:
if ele<=N: out[ele-1] += 1
else: out = [max(out) for i in range(len(out))]
return out
Python 100%
The following solution is simpler and easy to understand from the previous solutions in python where you can use set() to add a possible maximum number each time you find a new possible one. And after that when A[i] is equal to N + 1 you update counters with the max number found in the set and reset it again since the old max would be smaller than the coming ones and not needed. So the line where we clear the set is very important to pass all performance tests
Detected time complexity is:
O(N + M)
def solution(N, A):
counters = [0] * N
max_numbers = set()
for i in range(0, len(A)):
if 1 <= A[i] <= N:
index = A[i]-1
counters[index] += 1
max_numbers.add(counters[A[i]-1])
elif A[i] == N + 1 and len(max_numbers) > 0:
counters = [max(max_numbers)] * N
max_numbers.clear()
return counters
100% solution with Python–what helped was to keep track of the max in each iteration instead of calculating it each time N+1 appeared
def solution(N, A):
counters = [0] * N
all_max = list(set(A))
if len(all_max) == 1 and all_max[0] == N + 1:
return counters
the_max = 0
for i in A:
if i == N + 1:
counters = [the_max] * N
elif i > N + 1:
continue
else:
counters[i-1] += 1
if counters[i-1] > the_max:
the_max = counters[i-1]
return counters
Python
from collections import Counter
def solution(N, A):
stop = [i for i in range(len(A)) if A[i] > N]
old = val = 0
for s in stop:
o = A[old:s]
if o:
val += max(Counter(o).values())
old = s + 1
c = [val] * N
start = 0 if not stop else stop[-1] + 1
for i in A[start:]:
c[i-1] += 1
return c
So I’ve been going through the tests on codility and got bit stuck with the “Max Counters” one (link https://codility.com/demo/take-sample-test/max_counters). My first, and obvious solution was the following one:
def solution(N, A):
counters = N * [0];
for a in A:
if 1 <= a <= N:
counters[a - 1] += 1;
elif a == N + 1:
counters = N * [max(counters)];
return counters
which works just fine, but takes too much time, due to the fact that each call to max counters fills an entire array.
So I came up with the following solution which seems to work ok for small inputs, but randomly provides incorrect results for medium and large ones.
def solution(N, A):
counters = N * [0];
current_max = 0;
last_update = 0;
for a in A:
if 1 <= a <= N:
counters[a - 1] += 1;
if counters[a - 1] < last_update:
counters[a - 1] = last_update + 1;
if counters[a - 1] > current_max:
current_max = counters[a - 1];
elif a == N + 1:
last_update = current_max;
for i in xrange(len(counters)):
if counters[i] < last_update:
counters[i] = last_update;
return counters
I can’t seem to figure out what’s wrong with it.
Edit: Result – http://codility.com/demo/results/demoQA7BVQ-NQT/
One problem is here:
counters[a - 1] += 1
if counters[a - 1] < last_update:
counters[a - 1] = last_update + 1
what if counters[a - 1]
was last_update - 1
?
You can take a look at my solution(written in C# though):
public static int[] solution(int N, int[] A)
{
// write your code in C# with .NET 2.0
var counters = new int[N];
var defaultValueToInitialize = 0;
var maxElement = 0;
//initializing the counters values, without increasing the N+1 actions
foreach (var num in A)
{
if (num == N + 1)
{
defaultValueToInitialize = maxElement;
counters = new int[N];
}
else
{
counters[num - 1]++;
if (counters[num - 1] + defaultValueToInitialize > maxElement)
maxElement = counters[num - 1] + defaultValueToInitialize;
}
}
//adding the increased default value to each cell
for (int i = 0; i < counters.Length; i++)
{
counters[i] += defaultValueToInitialize;
}
return counters;
}
Check this one (python, get’s 100 score):
The secret is not to update all the counters every time you get the instruction to bump them all up to a new minimum value. This incurs an operation involving every counter on every occasion, and is the difference between a ~60% score and a 100% score.
Instead, avoid this hit by keeping track of the current minimum and maximum values; using and updating them for each counter you visit.
Then, after all the instructions are processed, because there may be counters which haven’t been touched with their own personal update since the last update-all instruction, pass over the counters themselves and ensure they are at the minimum value.
def solution(N, A):
res = [0] * N
max_val = 0
last_update = 0
n1 = N+1
for i in A:
if i < n1:
if res[i-1] < last_update:
res[i-1] = last_update
res[i-1]+=1
if res[i-1] > max_val:
max_val = res[i-1]
else:
last_update = max_val
for i in xrange(len(res)):
if res[i] < last_update:
res[i] = last_update
return res
Consider this 100/100 solution in Ruby:
# Algorithm:
#
# * Maintain a maximum value.
# * For each `increase(X)` command update respective counter.
# * For each `max_counter` command save the current max as `set_max` for later use.
# * Once the loop is over, make an adjustment pass to set all values less than `set_max` to `set_max`.
def solution(n, commands)
max = set_max = 0
counters = Array.new(n, 0)
commands.each do |cmd|
if cmd <= n
# This is an `increase(X)` command.
value = [counters[cmd - 1], set_max].max + 1
counters[cmd - 1] = value
max = [value, max].max
else
# This is a `max_counter` command.
# Just update `set_max`.
set_max = max
end
end
# Finalize -- set counters less than `set_max` to `set_max`.
counters.map! {|value| [value, set_max].max}
# Result.
counters
end
#--------------------------------------- Tests
def test
sets = []
sets << ["1", [1], 1, [1]]
sets << ["sample", [3, 2, 2, 4, 2], 5, [3, 4, 4, 6, 1, 4, 4]]
sets.each do |name, expected, n, commands|
out = solution(n, commands)
raise "FAILURE at test #{name.inspect}: #{out.inspect} != #{expected.inspect}" if out != expected
end
puts "SUCCESS: All tests passed"
end
My python version only scores 66 points as it’s a little too slow for the later tests.
def solution(N, A):
counters = [0 for x in range(N)]
for elem in A:
if elem > N:
cmax = max(counters)
counters = [cmax for x in range(N)]
else:
counters[elem-1] += 1
return counters
100/100 solution in C
struct Results
solution(int N, int A[], int M)
{
struct Results result;
int *cnts = calloc(N, sizeof(int));
int i = 0, maxcnt = 0, j = 0, lastcnt = 0;
for (i = 0; i < M; i++) {
if (A[i] <= N && A[i] >= 1) {
if (cnts[A[i] - 1] < lastcnt)
cnts[A[i] - 1] = lastcnt + 1;
else
cnts[A[i] - 1] += 1;
if (cnts[A[i] - 1] > maxcnt)
maxcnt = cnts[A[i] - 1];
}
if (A[i] == N + 1)
lastcnt = maxcnt;
}
for (j = 0; j < N; j++) {
if (cnts[j] < lastcnt)
cnts[j] = lastcnt;
}
result.C = cnts;
result.L = N;
return result;
}
MaxCounters solution in C
struct Results solution(int N, int A[], int M) {
struct Results result;
// write your code in C90
int i,k=0,max_v=0;
result.C =(int*)(malloc(N*sizeof(int)));
result.L = N;
memset(result.C, 0, N*sizeof(int));
for(i=0;i<M;i++)
{
if (A[i] > N)
max_v=k;
else
{
if(result.C[A[i]-1] < max_v)
result.C[A[i]-1]=max_v;
result.C[A[i]-1]+=1;
if(result.C[A[i]-1] > k)
k=result.C[A[i]-1];
}
}
for(i=0;i<N;i++)
{
if(result.C[i] < max_v)
result.C[i]=max_v;
}
return result;
}
Javascript 100/100
function solution(N, A) {
var j, len = A.length, lastBase = 0, max = 0,
counters = [], n1 = N+1;
for(j=0; j<N; j+=1){
counters[j]=0; //initArray
}
for(j=0; j < len; j+=1){
if(A[j]<n1){
if(counters[A[j]-1] < lastBase) {
counters[A[j]-1] = lastBase;
}
counters[A[j]-1] += 1;
if(max < counters[A[j]-1]) {
max = counters[A[j]-1];
}
} else {
lastBase = max;
}
}
for(j=0; j<N; j+=1){
if(counters[j] < lastBase) {
counters[j] = lastBase;
}
}
return counters;
}
This is a modified version of @jacoor’s solution with slightly more idiomatic python and variable names and if statement conditions more closely reflecting the problem description.
def fast_solution(N, A):
counters = [0] * N
max_counter = 0
last_update = 0
for K,X in enumerate(A): # O(M)
if 1 <= X <= N:
counters[X-1] = max(counters[X-1], last_update)
counters[X-1] += 1
max_counter = max(counters[X-1], max_counter)
elif A[K] == (N + 1):
last_update = max_counter
for i in xrange(N): # O(N)
counters[i] = max(counters[i], last_update)
return counters
Javascript 100/100
function solution(N, A) {
var max = 0,
offset = 0,
counters = Array.apply(null, Array(N)).map(function () {return 0;});
A.forEach(function (d) {
if (d === N + 1) {
offset = max;
}
else {
counters[d-1] = Math.max(offset + 1, counters[d-1] + 1);
max = Math.max(counters[d-1], max);
}
});
counters.map(function (d, i) {
if (d < offset) {
counters[i] = offset;
}
});
return counters;
}
Java solution:
public int[] solution(int N, int[] A) {
// write your code in Java SE 8
int[] counter = new int[N];
int maxCounter = 0;
int pos;
int last_max=0;
for (int i = 0; i < A.length; i++) {
if (A[i] <= N) {
pos = A[i];
if (counter[pos - 1] < last_max)
counter[pos - 1] = last_max;
counter[pos - 1] += 1;
if (maxCounter < counter[pos - 1])
maxCounter = counter[pos - 1];
}
else{
last_max=maxCounter;
}
}
for (int i = 0; i < counter.length; i++) {
if (counter[i] < last_max)
counter[i] = last_max;
}
return counter;
}
C++ 100/100
The key is to IGNORE the sample iteration in the problem, it will lead you to a O(m*n) time complexity solution.
vector<int> solution(int N, vector<int> &A) {
// write your code in C++11
vector<int> counters(N,0);
int last_reset = 0, max_count = 0;
for( unsigned int a=0; a < A.size(); ++a)
{
int current_int = A.at (a);
if (current_int == (N+1))
{
last_reset = max_count;
}
else
{
unsigned int counter_index = current_int - 1;
if ( counters.at (counter_index) < last_reset)
counters.at (counter_index) = last_reset + 1;
else
++counters.at (counter_index);
if ( counters.at (counter_index) > max_count)
max_count = counters.at (counter_index);
}
}
for( unsigned int n=0; n < counters.size(); ++n)
{
if ( counters.at (n) < last_reset)
counters.at (n) = last_reset;
}
return counters;
}
C# – a 100/100 solution
public int[] solution(int N, int[] A) {
// write your code in C# 6.0 with .NET 4.5 (Mono)
int[] counter = new int[N];
int maxValue = 0;
int minValue = 0;
for(int i=0;i<A.Length;i++)
{
//less than or equal to length N
if(A[i] <= N)
{
if(counter[A[i] - 1] < minValue)
{
counter[A[i] - 1] = minValue;
}
counter[A[i] - 1] += 1;
if(counter[A[i] - 1] > maxValue)
{
maxValue = counter[A[i] - 1];
}
}
else if(A[i] == N+1)
{
minValue = maxValue;
}
}
for(int j=0;j<counter.Length;j++)
{
if(counter[j] < minValue)
{
counter[j] = minValue;
}
}
return counter;
}
here is the shortest solution in Python:
def solution(N, A):
out = [0 for _ in range(N)]
for ele in A:
if ele<=N: out[ele-1] += 1
else: out = [max(out) for i in range(len(out))]
return out
Python 100%
The following solution is simpler and easy to understand from the previous solutions in python where you can use set() to add a possible maximum number each time you find a new possible one. And after that when A[i] is equal to N + 1 you update counters with the max number found in the set and reset it again since the old max would be smaller than the coming ones and not needed. So the line where we clear the set is very important to pass all performance tests
Detected time complexity is:
O(N + M)
def solution(N, A):
counters = [0] * N
max_numbers = set()
for i in range(0, len(A)):
if 1 <= A[i] <= N:
index = A[i]-1
counters[index] += 1
max_numbers.add(counters[A[i]-1])
elif A[i] == N + 1 and len(max_numbers) > 0:
counters = [max(max_numbers)] * N
max_numbers.clear()
return counters
100% solution with Python–what helped was to keep track of the max in each iteration instead of calculating it each time N+1 appeared
def solution(N, A):
counters = [0] * N
all_max = list(set(A))
if len(all_max) == 1 and all_max[0] == N + 1:
return counters
the_max = 0
for i in A:
if i == N + 1:
counters = [the_max] * N
elif i > N + 1:
continue
else:
counters[i-1] += 1
if counters[i-1] > the_max:
the_max = counters[i-1]
return counters
Python
from collections import Counter
def solution(N, A):
stop = [i for i in range(len(A)) if A[i] > N]
old = val = 0
for s in stop:
o = A[old:s]
if o:
val += max(Counter(o).values())
old = s + 1
c = [val] * N
start = 0 if not stop else stop[-1] + 1
for i in A[start:]:
c[i-1] += 1
return c