Algorithm: Max Counters

匿名 (未验证) 提交于 2019-12-03 02:49:01

问题:

I have the following problem:

You are given N counters, initially set to 0, and you have two possible operations on them:

A non-empty zero-indexed array A of M integers is given. This array represents consecutive operations:

  • if A[K] = X, such that 1 ≤ X ≤ N, then operation K is increase(X),
  • if A[K] = N + 1 then operation K is max_counter.

For example, given integer N = 5 and array A such that:

A[0] = 3 A[1] = 4 A[2] = 4 A[3] = 6 A[4] = 1 A[5] = 4 A[6] = 4 

the values of the counters after each consecutive operation will be:

(0, 0, 1, 0, 0) (0, 0, 1, 1, 0) (0, 0, 1, 2, 0) (2, 2, 2, 2, 2) (3, 2, 2, 2, 2) (3, 2, 2, 3, 2) (3, 2, 2, 4, 2) 

The goal is to calculate the value of every counter after all operations.

I did the following solution but it runs at O(NK) where K = length of array A.

public int[] solution(int N, int[] A) {     int[] result = new int[N];     int maximum = 0;      for (int K = 0; K < A.Length; K++)     {         if (A[K] < 1 || A[K] > N + 1)             throw new InvalidOperationException();          if (A[K] >= 1 && A[K] <= N)         {             result[A[K] - 1]++;              if (result[A[K] - 1] > maximum)             {                 maximum = result[A[K] - 1];             }         }         else         {             // inefficiency here             for (int i = 0; i < result.Length; i++)                 result[i] = maximum;         }     }      return result; } 

Could anyone show me how this can be better done with O(N + K) where K is the length of array A? Sorry for may terrible coding, I am doing these exercises to improve my programming. Thanks!

回答1:

This is what I came up with, but I am not sure if it works 100%:

public int[] solution(int N, int[] A) {     int[] result = new int[N];     int maximum = 0;     int resetLimit = 0;      for (int K = 0; K < A.Length; K++)     {         if (A[K] < 1 || A[K] > N + 1)             throw new InvalidOperationException();          if (A[K] >= 1 && A[K] <= N)         {             if (result[A[K] - 1] < resetLimit) {                 result[A[K] - 1] = resetLimit + 1;             } else {                 result[A[K] - 1]++;             }              if (result[A[K] - 1] > maximum)             {                 maximum = result[A[K] - 1];             }         }         else         {             // inefficiency here             //for (int i = 0; i < result.Length; i++)             //    result[i] = maximum;             resetLimit = maximum;         }     }      for (int i = 0; i < result.Length; i++)         result[i] = Math.Max(resetLimit, result[i]);      return result; } 


回答2:

def solution(N, A):     # write your code in Python 2.6     res = [0] * N     m = 0     minValue = 0     for x in A:         if 1 <= x <= N:             res[x - 1] = max(res[x - 1], minValue) + 1             if res[x - 1] > m:                 m = res[x - 1]         else:             minValue = m     for i in xrange(N):         res[i] = max(res[i], minValue)     return res 


回答3:

Here's a solution I came up with in Python (100/100 on codility); it's a little different than others I've seen on here so thought I'd share:

def solution(N, A):     count = [0] * N     max_counter = [i for i, a in enumerate(A) if a == N+1]     if len(max_counter) == len(A):         return count     if max_counter:         mode = 0         for i, m in enumerate(max_counter):             if m == 0 or m - max_counter[i-1] == 1:                 continue             subcount = {}             if i == 0:                 for k in A[:m]:                     if k not in subcount:                         subcount[k] = 1                     else:                         subcount[k] += 1             else:                 for k in A[max_counter[i-1]+1:m]:                     if k not in subcount:                         subcount[k] = 1                     else:                         subcount[k] += 1             mode += max(subcount.values())         count = [mode] * N         for k in A[max_counter[-1]+1:]:             count[k-1] += 1     else:         for k in A:             count[k-1] += 1     return count 


回答4:

Remember:

"Making your code readable is as important as making it executable."

-- Robert C Martin

Even when trying to solve a hard problem...

So trying to achieve a better readability I've created a class to encapsulate the counters array and its operations (Law of Demeter). Sadly my first solution got only 60% in the performance test, so at the cost of a bit of readability I've improved it with a smarter solution and finally got 100%.

Here are my two implementations with comments:

O(N*M) Correctness 100% / Performance 60% (high redability)

//I didn't refactored the names of the variables N and A //to maintain it aligned with the question description public int[] solution(int N, int[] A) {     var counters = new Counters(N);      for (int k = 0; k < A.Length; k++)     {         if (A[k] <= N)             counters.IncreaseCounter(A[k]);         else             counters.MaxAllCounters();     }      return counters.ToArray(); }  public class Counters {     private int[] counters;     private int greaterValueInCounter = 0;      public Counters(int length)     {         counters = new int[length];     }      public void MaxAllCounters()     {         for (int i = 0; i < counters.Length; i++)         {             counters[i] = greaterValueInCounter;         }     }      public void IncreaseCounter(int counterPosition)     {         //The counter is one-based, but our array is zero-based         counterPosition--;          //Increments the counter         counters[counterPosition]++;          if (counters[counterPosition] > greaterValueInCounter)             greaterValueInCounter = counters[counterPosition];     }      //The counters array is encapsuled in this class so if we provide external      //acess to it anyone could modify it and break the purpose of the encapsulation     //So we just exposes a copy of it :)     public int[] ToArray()     {         return (int[])counters.Clone();     } }  

Codility result

O(N+M) Correctness 100% / Performance 100% (not so high redability)

Note the beauty of the encapsulation: to improve the algorithm I just have to edit some methods of the Counters class without changing a single character on the solution method.

Methods edited in the Counters class:

  • IncreaseCounter()
  • MaxAllCounters()
  • ToArray()

Final code:

//Exactly the same code public int[] solution(int N, int[] A) {     var counters = new Counters(N);      for (int k = 0; k < A.Length; k++)     {         if (A[k] <= N)             counters.IncreaseCounter(A[k]);         else             counters.MaxAllCounters();     }      return counters.ToArray(); }  public class Counters {     private int[] counters;     private int greaterValueInCounter = 0;     private int currentEquilibratedScore = 0;      public Counters(int length)     {         counters = new int[length];     }      public void MaxAllCounters()     {         //We don't update the entire array anymore - that was what caused the O(N*M)         //We just save the current equilibrated score value         currentEquilibratedScore = greaterValueInCounter;     }      public void IncreaseCounter(int counterPosition)     {         //The counter is one-based, but our array is zero-based         counterPosition--;          //We need to add this "if" here because with this new solution the array         //is not always updated, so if we detect that this position is lower than         //the currentEquilibratedScore, we update it before any operation         if (counters[counterPosition] < currentEquilibratedScore)             counters[counterPosition] = currentEquilibratedScore + 1;         else             counters[counterPosition]++;          if (counters[counterPosition] > greaterValueInCounter)             greaterValueInCounter = counters[counterPosition];     }      //The counters array is encapsuled in this class so if we provide external      //acess to it anyone could modify it and break the purpose of the encapsulation     //So we just exposes a copy of it :)     public int[] ToArray()     {         //Now we need to fix the unupdated values in the array         //(the values that are less than the equilibrated score)         for (int i = 0; i < counters.Length; i++)         {             if (counters[i] < currentEquilibratedScore)                 counters[i] = currentEquilibratedScore;         }          return (int[])counters.Clone();     } } 

Codility result



回答5:

Let's see...

public int[] Solution(int N, int[] A) {     int[] data = new int[N];     int maxval = 0;     int baseval = 0;     for (int K = 0; K < A.length; K++)     {         int index = A[K] - 1;         if (index < 0 || index > N)             throw new InvalidOperationException();          if (index < N)             maxval = baseval + Math.Max(maxval, ++data[index]);         else         {             baseval = maxval;             data = new int[N];         }     }      for (int K = 0; K < N; K++)         data[K] += baseval;      return data; } 

I think that's O(N+K). Depends on how you count the Order of re-initializing the array.



回答6:

Same principle as everybody scoring 100% really, it is just that I find this version easier to read (and it is probably only because I wrote it).

using System; using System.Linq;  class Solution  {     public int[] solution(int N, int[] A)      {          var currentMax = 0;         var resetValue = 0;         var counters = Enumerable.Range(1, N).ToDictionary(i => i, i => 0);          foreach (var a in A)         {             if (a == N + 1) resetValue = currentMax;             else             {                 counters[a] = Math.Max(counters[a], resetValue) + 1;                 currentMax = Math.Max(currentMax, counters[a]);             }         }         return counters.Values.Select(v => Math.Max(v,resetValue)).ToArray();     } } 


回答7:

public int[] counters(int N, int[] A) {     int[] count = new int[N];     int maxCount = 0;     int setAll = 0;      for (int i = 0; i < A.Length; i++)     {         if (A[i] == N + 1)         {             setAll += maxCount;             maxCount = 0;             count = new int[N];         }         else         {             count[A[i] - 1] += 1;               if (count[A[i] - 1] > maxCount)             {                 maxCount = count[A[i] - 1];             }         }     }      for (int j = 0; j < count.Length; j++)     {         count[j] += setAll;     }      return count; } 

I think this is O(N+K), but codility say its O(N*K)? Would appreciate if anyone could explain which is correct...



回答8:

A 100/100 solution in php

function solution($N, $A){     $cond     = $N + 1;     $cur_max  = 0;     $last_upd = 0;     $cnt_arr  = array();     $cnt      = count($A);     for($i = 0; $i < $cnt; $i++){         $cur = $A[$i];         if($cur == $cond){             $last_upd = $cur_max;         }         else{             $pos = $cur - 1;             if(!isset($cnt_arr[$pos])){                 $cnt_arr[$pos] = 0;             }             if($cnt_arr[$pos] < $last_upd){                 $cnt_arr[$pos] = $last_upd + 1;             }             else{                 $cnt_arr[$pos] ++;             }             if($cnt_arr[$pos] > $cur_max){                 $cur_max = $cnt_arr[$pos];             }         }     }     for($i = 0; $i < $N; $i++){         if(!isset($cnt_arr[$i])){             $cnt_arr[$i] = 0;         }         if($cnt_arr[$i] < $last_upd){             $cnt_arr[$i] = $last_upd;         }     }     return $cnt_arr; } 


回答9:

Rue, I just ran this locally. Watched the counters myself. I used this algorithm:

    public int[] solution(int N, int[] A)     {         int[] result = new int[N];         int maximum = 0;         int resetlimit = 0;          for (int K = 0; K < A.Length; K++)         {             if (A[K] < 1 || A[K] > N + 1)             {                 throw new InvalidOperationException();             }              if (A[K] >= 1 && A[K] <= N)             {                 if (result[A[K] - 1] < resetlimit)                 {                     result[A[K] - 1] = resetlimit + 1;                 }                 else                 {                     result[A[K] - 1]++;                 }                  if (result[A[K] - 1] > maximum)                 {                     maximum = result[A[K] - 1];                 }             }             else             {                 resetlimit = maximum;                 result = Enumerable.Repeat(maximum, result.Length).ToArray();             }         }          //for (int i = 0; i < result.Length; i++)         //{         //    result[i] = Math.Max(resetlimit, result[i]);         //}          return result;     } } 

looking at the problem and the result sets, you must include in the inefficient for loop in the else statement. The for loop outside does not replicate the 2nd operation

In order for iteration A[3] = 6 to set result[] all to '2' you must load the result array with the maximum counter. Otherwise your return will never have (2,2,2,2,2) as the 4th example array shows.

I too must take a test to get my dream job so the little inefficiency here is important;

the statement

  result = Enumerable.Repeat(maximum, result.Length).ToArray(); 

loads the array all in one shot so no inner loop and no inner efficiency. I think that is pretty close to the correct result sets. I'm surprised they didn't ask to return like a jagged array of the total return. Still, this codility test scares me a lot.



回答10:

C++11 code:

#include <algorithm>  vector<int> solution(int N, vector<int> &A) {      vector<int> hist(N, 0);      int last_update = 0;     int max_value = 0;      for (auto i : A){          if (1 <= i && i <= N){             int& val = hist[i - 1];              if (val < last_update)                 val = last_update + 1;             else                 val++;              if (max_value < val)                 max_value = val;         }          if (i == (N+1)){             last_update = max_value;         }      }      replace_if(hist.begin(), hist.end(), [last_update](int x){return x < last_update;}, last_update);      return hist; } 


回答11:

The key is that [0] * N is an N operation. If that exists in a for loop it will become N*M. Tested in Codility 100%

            # you can write to stdout for debugging purposes, e.g.             # print "this is a debug message"              def solution(N, A):                 # write your code in Python 2.7                 count = [0] * N                 maxCounter = 0                 minCounter = 0                 for x in A:                     if x <= N and x >= 1:                         count[x-1] = max(count[x-1], minCounter) + 1                         if maxCounter < count[x-1]:                             maxCounter = count[x-1]                     if x == N + 1:                         minCounter = maxCounter                                 for i in xrange(N):                     count[i] = max(count[i], minValue)                 return count 


回答12:

Here is Scala version, 100% on codility:

import java.util  def solution(N: Int, A: Array[Int]): Array[Int] = {      var counters = new Array[Int](N)      var maxCounter = 0      for(ind <- 0 to A.length-1){         if(A(ind) == (N+1)){          //all to max         util.Arrays.fill(counters,maxCounter)        }else{         //ind -1  cause array index start with 0 which is marked as 1 in the input data         counters(A(ind)-1) += 1          //update max counter if necessary         if(maxCounter< (counters(A(ind)-1))) maxCounter = (counters(A(ind)-1))        }      }      return counters   } 

Performance: https://codility.com/demo/results/trainingKJT6Y3-74G/



回答13:

Ruby Codility Code that got 100/100

def solution(a)    if a.length < 3       0   end   a.sort!   for i in 2..a.length - 1     if (a[i-2] + a[i-1]) > a[i]       return 1     end   end  0 end 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!