Выбор чисел — решите задачу о ранге хакера в Ruby

#ruby

#рубиновый

Вопрос:

Объяснение проблемы

Учитывая массив целых чисел, найдите самый длинный подмассив, в котором абсолютная разница между любыми двумя элементами меньше или равна 1.

Пример a = [1,1,2,2,4,4,5,5,5] Есть два подмассива, удовлетворяющие критерию: [1,1,2,2] и [4,4,5,5,5]. Подмассив максимальной длины состоит из 5 элементов.

pickingNumbers имеет следующие параметры(ы):

  • int a[n]: массив целых чисел

ВОЗВРАТ

  • int: длина самого длинного подмассива, который соответствует критерию

ТО, что я пробовал

 def pickingNumbers(a)
    # Write your code here
    a.sort!
    current_counter = 0
    max_counter = 0
    
    i = 0
    while i < a.size
      j = 1
      
      while (a[j].to_int - a[i].to_int) <= 1
        current_counter  = 1
        j  = 1
        max_counter = current_counter if current_counter > max_counter
      end
      
      i =1
    end
    max_counter
end

input = [1,1,2,2,4,4,5,5,5]
pickingNumbers(input)
 

Запрошенный:

Почему приведенный выше код не сработал? Я был бы признателен, если кто-нибудь сможет объяснить. Я учусь решать алгоритмические задачи и знаю, что есть десятки лучших решений. Тем не менее, я хотел бы знать, почему именно этот код не работает!)) Заранее спасибо

Комментарии:

1. undefined method 'to_int' for nil:NilClass по причине, объясненной Мэтью здесь ниже. Совет по алгоритму: взгляните на ruby-doc.org/core-3.0.0/Enumerable.html#method-i-tally

2. «Почему приведенный выше код не сработал?» — недостаточно точное описание ошибки, чтобы мы могли вам помочь. Что не работает? Как это не работает? Какие проблемы у вас с вашим кодом? Вы получаете сообщение об ошибке? Что это за сообщение об ошибке? Является ли результат, который вы получаете, не тем результатом, который вы ожидаете? Какого результата вы ожидаете и почему, какой результат вы получаете и чем они отличаются? Является ли поведение, которое вы наблюдаете, не желаемым поведением? Каково желаемое поведение и почему, каково наблюдаемое поведение и чем они отличаются?

3. Можете ли вы предоставить ссылку на проблему с Hackerrank? Если я правильно понимаю, вам нужен подмассив a , который представляет собой массив формы a[i..j] , поэтому я не понимаю, как вы можете сортировать a .

Ответ №1:

Вы проверяете, чтобы убедиться i < a.size , но вы не делаете то же самое для j .

В конечном итоге вы увеличиваете j за пределы размера массива и пытаетесь получить доступ a[9] , который возвращает nil.

nil.to_int вызывает ошибку (хотя на самом деле вы не указали, какую ошибку вы видели в своем вопросе).

Более идиоматичным способом перебора массива в Ruby является использование .each , которое позволяет избежать этого класса ошибок.

Я бы также не советовал использовать .sort! параметр, который вы передали. Оператор ! bang в конце предупреждает вас, что это изменяет исходный массив.

Ответ №2:

Вот способ решить ее с большей временной сложностью, который позволяет избежать использования 2 циклов

Я знаю, что можно внести несколько изменений (например, с помощью троичного оператора и так далее). Вы можете цементировать, если у вас есть что-то на уме

 def pickingNumbers(a)
    # Write your code here
    a.sort!
    current_counter = 1
    max_counter = 0
    reference_checker = a[0]
    
    i = 1
    while i < a.size
    
      if (a[i] - reference_checker) <= 1
        current_counter  = 1
      else
        reference_checker = a[i]
        max_counter = current_counter if current_counter > max_counter
        current_counter = 1
      end
      
      i =1
    end
    if current_counter < max_counter
      max_counter
    else
      current_counter
    end
end

input = [1,1,2,2,4,4,5,5,5]
p pickingNumbers(input)
 

Спасибо моей команде поддержки)

Комментарии:

1. pickingNumbers [1,1,3,2,4,4,5,5,7,6,5,6,5,4,3,4,5,7,8] #=> 7 , но я не вижу подходящего подмассива размером больше 4.

Ответ №3:

Вот два способа решения проблемы: один легко кодируется, другой сложнее, но я ожидаю, что он должен быть быстрее, возможно, намного быстрее. Оба используют следующие массивы для демонстрации.

 a1 = [1,1,2,3,4]
a2 = [1,1,2,2,4,4,5,5,5]
a3 = [1,1,3,2,4,4,5,5,7,6,5,6,5,4,3,4,5,7,8]
 

Немного кода, но может быть достаточно быстрым

 def quick_n_dirty(arr)
  arr.size.downto(1).each do |n|
    arr.each_index.each_cons(n) do |a|
      mn, mx = a.map { |i| arr[i] }.minmax
      return { start: a.first, end: a.last } if mx <= mn   1
    end
  end
end
 
 quick_n_dirty(a1)
  #=> {:start=>0, :end=>2}
quick_n_dirty(a2)
  #=> {:start=>4, :end=>8}
quick_n_dirty(a3)
  #=> {:start=>4, :end=>7}
 

Смотрите Перечислимые #each_cons.

Рассмотрим a1 , который содержит пять элементов. Сначала посмотрите, соответствует ли весь массив требованиям, и в этом случае мы закончили бы:

 enum = a1.each_cons(5)
  #=> #<Enumerator: [1, 1, 2, 3, 4]:each_cons(5)>
 

Чтобы увидеть элементы, которые будут сгенерированы, enum мы можем преобразовать их в массив:

 enum.to_a
  #=> [[1, 1, 2, 3, 4]]
 

Он генерирует только один элемент, a1 сам:

 a = enum.next
  #=> [1, 1, 2, 3, 4]
b, c = a.minmax 
  #=> [1, 4]
 

Как (c-b).abs = 3 и 3 > 1 мы видим, a1 это не соответствует требованиям. Теперь рассмотрим все подмассивы размера 4 . Если найдено одно из них, обладающее желаемым свойством, мы закончили.

 enum = a1.each_cons(4)
  #=> #<Enumerator: [1, 1, 2, 3, 4]:each_cons(4)>
enum.to_a
  #=> [[1, 1, 2, 3], [1, 2, 3, 4]]
a = enum.map { |arr| arr.minmax }
  #=> [[1, 3], [1, 4]]
b = a.map { |x,y| (y-x).abs }
  #=> [2, 3]
b.find { |diff| diff <= 1 }
   #=> nil
 

Это говорит нам о том, что нет подмассива размера 4 , соответствующего требованию, поэтому мы продолжаем рассматривать подмассивы размера 3 :

 enum = a1.each_cons(3)
  #=> #<Enumerator: [1, 1, 2, 3, 4]:each_cons(3)>
enum.to_a
  #=> [1, 1, 2], [1, 2, 3], [2, 3, 4]]
a = enum.map { |arr| arr.minmax }
  #=> [[1, 2], [1, 3], [2, 4]]
b = a.map { |x,y| (y-x).abs }
  #=> [1, 2, 2]
b.find { |diff| diff <= 1 }
  #=> 1
 

Плати грязью! Мы находим, что подмассив [1, 1, 2] соответствует требованию, поэтому мы закончили!

Это то, что делает мой код, за исключением того, что он возвращает хэш, показывающий начало и конец (или я должен сказать «a», а не «the», поскольку могут быть связи) наибольшего подмассива с желаемым свойством. (При повторном прочтении вопроса выясняется, что требуется только длина самого большого подмассива, что допускает незначительное упрощение.)

Если n = arr.size , в худшем случае, количество подмассивов, которые должны быть проверены, равно n (n-1) (n-2) ... 2 1 , что равно n*(n 1)/2 .

Больше кода, но он должен быть быстрее

 def should_be_faster(arr)
  a = arr   [Float::INFINITY]
  current = { early: 0, late: nil }
  a.each_with_index.with_object({ start: 0, end: 0 }) do |(n,i), longest|
    next if i.zero?               
    curr_val = a[i]
    curr_early_val = a[current[:early]]
    curr_late_val = current[:late].nil? ? nil : a[current[:late]]
    if current[:late].nil?
      case curr_val - curr_early_val
      when 0
      when 1, -1
        current[:late] = i
      else
        update_if_improvement(current, longest, i)
        current[:early] = i
        current[:late] = nil
      end
    elsif curr_val != curr_early_val amp;amp; curr_val != curr_late_val
      update_if_improvement(current, longest, i)
      if (curr_val - curr_late_val).abs == 1
        current[:early] = current[:late]
        current[:late] = i
      else
        current[:early] = i
        current[:late] = nil
      end
    end
  end
end
 
 def update_if_improvement(current, longest, i)
  if i - current[:early] > longest[:end] - longest[:start]   1
    longest[:start] = current[:early]
    longest[:end] = i-1
  end
end
 
 should_be_faster(a1)
  #=> {:start=>0, :end=>2}
should_be_faster(a2)
  #=> {:start=>4, :end=>8}
should_be_faster(a3)
  #=> {:start=>4, :end=>7}
 

Комментарии:

1. Я дам объяснение решению № 2, когда позволит время.

Ответ №4:

У меня есть процедура, которая разделит массив на группы, так что [1,1,1,2,2,2,3,3,3] будет 2 массива [1,1,1,2,2,2] и [3,3,3]

очевидно, что если это было для hackerrank, вам нужно посмотреть, достаточно ли это быстро.

если вы хотите ужасный однострочник:

 a.sort.inject({}) {|x,y| x.key?(y-1) ? x[y-1] << y : (x[y]||=[] ; x[y] << y) ; x}.values.map(amp;:count).max
 

или как более длительная процедура:

 def pickingNumbers(a)
  # sort the array and work through the array with an accumulator
  newh = a.sort.each_with_object({}) do |value, acc|
    # check if we had a previous value
    if acc.key?(value - 1)
      # push the current value into the array for the previous value
      acc[value - 1] << value
    else
      # make sure that the "acc[value]" is an array
      acc[value] ||= []
      # push the current value into the value for the current value
      acc[value] << value
    end
  end

  # get the size of all of the values in the newly created hash
  vals = newh.values.map(amp;:count)

  # return the maximum of that array
  vals.max
end

input = [1,1,2,2,4,4,5,5,5]
pickingNumbers(input)