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/

Asked By: jaho

||

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?

Answered By: jonrsharpe

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;
    }
Answered By: IdoT

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

http://codility.com/demo/results/demoF3AMPT-FVN/

Answered By: jacoor

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
Answered By: Alex Fortuna

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
Answered By: manonthemat

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;
}
Answered By: ramrunner

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;
}
Answered By: Balaji Reddy

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;
    
}

Answered By: SntsDev

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

https://codility.com/demo/results/demo6KPS7K-87N/

Answered By: Al Johri

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;
}
Answered By: piUek

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;
}
Answered By: Chuddamani

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;

}
Answered By: Tom Serink

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;
}
Answered By: albtvenki

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
Answered By: vagitus

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
Answered By: gerardstreet

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
Answered By: Aleksey
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.