Codility: CountDistinctSlices. What am I missing? - ruby

The Codility Question Is Here: https://codility.com/programmers/lessons/15-caterpillar_method/count_distinct_slices/
Now, my solution is below:
def solution(m, a)
end_idx = 0
hash_of_elements = {}
last_idx = a.size - 1
slice_right_now = []
slice_counter = 0
while last_idx >= end_idx
el_to_add = a[end_idx]
while !hash_of_elements[el_to_add].nil?
element_to_remove = slice_right_now.shift
hash_of_elements.delete element_to_remove
#puts "removing #{element_to_remove} from the slice. the new slice is #{slice_right_now}. Hash is #{hash_of_elements.inspect}"
puts "#{slice_right_now.inspect}" if slice_right_now.size > 1
if slice_right_now.size > 1
slice_counter += 1
return 1000000000 if slice_counter > 1000000000
end
end
#puts "Adding #{el_to_add} to the list!"
hash_of_elements[el_to_add] = true
slice_right_now << el_to_add
puts "#{slice_right_now.inspect}" if slice_right_now.size > 1
if slice_right_now.size > 1
slice_counter += 1
return 1000000000 if slice_counter > 1000000000
end
end_idx += 1
end
puts "Number of slices other than indivisual elments are #{slice_counter}"
slice_counter += a.size
end
It is a Ruby Solution. For the input: 6, [1, 3, 4, 1, 2, 1, 3, 2, 1]
It gets the following slices:
[1, 3]
[1, 3, 4]
[3, 4]
[3, 4, 1]
[3, 4, 1, 2]
[4, 1, 2]
[1, 2]
[2, 1]
[2, 1, 3]
[1, 3]
[1, 3, 2]
[3, 2]
[3, 2, 1]
In addition to this, each element of the array is a slice also.
The answer is wrong however apparently.
The answer to that input is supposed to be 24. Mine is 22. I don't understand what I am missing.

24 is correct, as you can easily check with brute force solutions that go over all slices and count the distinct ones:
(1..a.size).sum { |k| a.each_cons(k).count { |s| !s.uniq! } }
=> 24
(1..a.size).sum { |k| a.each_cons(k).reject(&:uniq!).count }
=> 24
(0...a.size).sum { |i| (i...a.size).count { |j| !a[i..j].uniq! } }
=> 24
(0...a.size).to_a.repeated_combination(2).count { |i, j| !a[i..j].uniq! }
=> 24
(0..a.size).to_a.combination(2).count { |i, j| !a[i...j].uniq! }
=> 24
If you don't just count but print them, you'll see that you're missing the slice consisting of [4, 1] and the slice consisting of [2, 1] at the end.
The fishing lesson is: If the problematic case is small enough that you can solve it with trivial brute force, do do that and compare its findings with your more clever attempt's findings.

Related

Reversed sequence in Ruby

How do I return an array of integers from n to 1 where n>0? I wrote this code:
def reverse_seq(num)
reverse_seq = []
[].reverse { |num| num > 0; num += 1 }
return []
end
Thanks!
You could create an enumerator via downto that goes from n down to 1 and turn that into an array:
n = 5
n.downto(1).to_a
#=> [5, 4, 3, 2, 1]
or you could call Array.new with a block and calculate each value based on its index:
n = 5
Array.new(n) { |i| n - i }
#=> [5, 4, 3, 2, 1]
or you could traverse a n..1 range by passing -1 to step:
n = 5
(n..1).step(-1).to_a
#=> [5, 4, 3, 2, 1]
Or
(1..5).to_a.reverse
#=> [5, 4, 3, 2, 1]
Or if you want to iterate over those elements in a next step anyway, use reverse_each
(1..5).reverse_each { |i| puts i }
#=> 5
4
3
2
1
As of 2.7 you can also use Enumerator#produce which is my new favorite way to create sequences.
For your use case:
def reverse_seq(num)
Enumerator.produce(num) {|prev| prev.positive? ? prev.pred : raise(StopIteration) }
end

Birthday Chocolate HACKERRANK RUBY

This is the original link for the problem in hackerrank: https://www.hackerrank.com/challenges/the-birthday-bar/problem
I have been fighting with this problem in Ruby and I don't know why my counter always returns 1. This is the solution. I hope you can help me to understand what I'm making wrong.
s = [1, 2, 1, 3, 2]
d = 3
m = 2
def birthday(s, d, m)
array = []
cont = 0
sum = 0
m.times {array.push(s.shift)}
(m-1).times do
array.each {|i| sum = sum + i}
if sum == d
cont += 1
end
array.shift
array.push(s.shift)
end
return cont
end
birthday(s, d, m)
Though the following does not answer your question directly, it is a Ruby-like way of solving the problem, especially by making use of the methods Enumerable#each_cons and Enumerable#count.
def birthday(s, d, m)
s.each_cons(m).count { |a| a.sum == d }
end
s = [1, 2, 1, 3, 2]
d = 3
m = 2
birthday(s, d, m)
#=> 2 ([1, 2] and [2, ])
s = [2, 2, 1, 3, 2]
d = 4
m = 2
birthday(s, d, m)
#=> 2 ([2, 2] and [1, 3])
s = [2, 4, 3, 2, 1, 2, 6, 1]
d = 9
m = 3
birthday(s, d, m)
#=> 4 ([2, 4, 3], [4, 3, 2], [1, 2, 6] and [2, 6, 1])
Notice from the doc that when each_cons is used without a block it returns an enumerator:
s = [1, 2, 1, 3, 2]
d = 3
m = 2
enum = s.each_cons(m)
#=> #<Enumerator: [1, 2, 1, 3, 2]:each_cons(2)>
enum will generate elements and pass them to count until there are no more to generate, at which time it raises a StopIteration exception:
enum.next #=> [1, 2]
enum.next #=> [2, 1]
enum.next #=> [1, 3]
enum.next #=> [3, 2]
enum.next #=> StopIteration (iteration reached an end) <exception>
We can write1:
enum.count { |a| a.sum == d }
#=> 2
After enum generates the first value ([1, 2]) the block variable a is assigned its value:
a = enum.next
#=> [1, 2]
and the block calculation is performed. As
a.sum == d
#=> [1, 2].sum == 3 => true
the count is incremented (from zero) by one. enum then passes each of its remaining values to count and the process is repeated. When, for example, [1, 3].sum == 3 => false is executed, the count is not incremented.
1. Note that since I just stepped through all the elements of enum, enum.next would generate another StopIteration exception. To execute enum.count { |a| a.sum == d } I therefore must first redefine the enumerator (enum = s.each_cons(m)) or Enumerator#rewind it: enum.rewind.

leetcode first (the easiest) - two_sum

I wanted to practise some algorithms... Why doesn't my solution work on leetcode website?!?!
PS: Would be grateful for other resources to learn algorithms and practise interview questions.
# #param {Integer[]} nums
# #param {Integer} target
# #return {Integer[]}
def two_sum(nums, target)
i,j = 0,nums.length-1
output = []
while i < nums.length-1
while j > i
if nums[i] + nums[j] == target
output << i << j
end
j-=1
end
i+=1
end
output
end
Result from the website:
Input:
[3,2,4]
6
Output: []
Expected:[1,2]
Now that your question has been answered, I would like to suggest a more Ruby-like method.
Code
def two_sum(nums, target)
(0...nums.size).to_a.combination(2).find { |i,j| nums[i]+nums[j] == target }
end
Example
nums = [1,5,2,3,4]
target = 8
two_sum(nums, target)
#=> [1,3]
Explanation
For the example above, the steps are as follows:
a = nums.size
#=> 5
b = a.times
#=> #<Enumerator: 5:times>
c = b.to_a
#=> [0, 1, 2, 3, 4]
d = c.combination(2)
#=> #<Enumerator: [0, 1, 2, 3, 4]:combination(2)>
We can see the elements that are generated by the enumerator d by converting it to an array.
d.to_a
#=> [[0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
d.find { |i,j| nums[i]+nums[j] == target }
#=> [1, 3]
Note that (0...nums.size).to_a could be replaced by any of the following.
[*0...nums.size]
nums.each_index.to_a
nums.size.times.to_a
0.upto(nums.size-1).to_a
Array.new(nums.size) { |i| i }
Array.new(nums.size, &:itself)
Your error is that you don't reinitialize j when it reaches i which means that your algorithm just tries (0,n-1), (0,n-2), …, (0, 2), (0, 1) and then stops.

Find combinations in Ruby that are less than a certain number

Say I have an array [1,2,3] and I want every combination of these numbers that don't exceed 4. So I would have [1,2,3].someMethod(4) and it would give me:
[1,1,1,1]
[1,1,2]
[1,3]
[2,2]
So far I have:
(1..4).flat_map{|size| [1,2,3].repeated_combination(size).to_a }
but this gives me every possible combinations, including the ones that exceed my given limit. Is there an good way to either only get combinations that add up to my limit?
arr = [1,2,3]
(arr+[0]).repeated_combination(4).select { |a| a.reduce(:+) == 4 }.map { |a| a - [0] }
#=> [[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
Change == to <= if desired.
This answer, like the others, assumes arr contains natural numbers, including 1.
results = (1..4).each.with_object([]) do |size, results|
[1,2,3].repeated_combination(size) do |combo|
results << combo if combo.reduce(:+) == 4
end
end
p results
--output:--
[[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
Parameterizing the algorithm:
def do_stuff(values, target_total)
(1..target_total).each.with_object([]) do |size, results|
values.repeated_combination(size) do |combo|
results << combo if combo.reduce(:+) == 4
end
end
end
p do_stuff([1, 2, 3], 4)
You can filter out the arrays you don't want by using the select method. Just select all the arrays that have a sum == 4 (the sum is calculated by the inject method).
all_arrs = (1..4).flat_map do |size|
[1,2,3].repeated_combination(size).to_a
end
valid_arrs = all_arrs.select do |arr|
arr.inject { |a, b| a + b } == 4
end
print valid_arrs
# Output:
# [[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
A recursive approach.
def some_method(a, n)
return [[]] if n == 0
a.select { |e| e <= n }.\
flat_map { |e| some_method(a,n-e).map { |es| ([e] + es).sort } }.\
sort.\
uniq
end
p some_method([1,2,3], 4)
# => [[1, 1, 1, 1], [1, 1, 2], [1, 3], [2, 2]]
EDIT: Here is another recursive version without filtering duplicates but with opposite order. I added comments to make it clearer.
def some_method(a, n)
return [[]] if n == 0 # bottom (solution) found
return [] if a.empty? || n < 0 # no solution
max = a.max
# search all solutions with biggest value
l = some_method(a, n-max).map { |e| [max] + e }
# search all solutions without biggest value
r = some_method(a-[max],n)
l + r
end
p some_method([1,2,3], 4)
# => [[3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]

Get items from Ruby Array that occur 2 or more times

Let's say I have a Ruby array.
[1,2,3,4,4,5,6,6,7,7]
I want to find the values that occur 2 or more times.
[4,6,7]
It will help my process to first determine which items occur only once then remove those. So I'd like to solve this by first finding the items that occur once.
There are probably better ways, but this is one:
> [1,2,3,4,4,5,6,6,7,7].group_by{|i| i}.reject{|k,v| v.size == 1}.keys
=> [4, 6, 7]
Breaking it down:
> a = [1,2,3,4,4,5,6,6,7,7]
=> [1, 2, 3, 4, 4, 5, 6, 6, 7, 7]
> a1 = a.group_by{|i| i}
=> {1=>[1], 2=>[2], 3=>[3], 4=>[4, 4], 5=>[5], 6=>[6, 6], 7=>[7, 7]}
> a2 = a1.reject{|k,v| v.size == 1}
=> {4=>[4, 4], 6=>[6, 6], 7=>[7, 7]}
> a2.keys
=> [4, 6, 7]
Everyone loves a really difficult to follow one liner :)
[1,2,3,4,4,5,6,6,7,7].each_with_object(Hash.new(0)) { |o, h| h[o] += 1 }.select { |_, v| v > 1 }.keys
Add some white space and some comments
[1,2,3,4,4,5,6,6,7,7].each_with_object(Hash.new(0)) { |o, h|
h[o] += 1
}.select { |_, v|
v > 1
}.keys
Enumerate and pass in our memo hash to each iteration the Hash defaults to having 0 for any key
Increment counter for the object
Select only key value pairs where the value is greater than 1
Grab just the keys
This looks quite similar to Phillip's neat answer - in theory this should use slightly less memory as it will not have to build the intermediate arrays to perform counting
Another way:
a = [1,2,3,4,4,5,6,6,7,7]
au = a.uniq
a.reject { |i| au.delete(i) }
#=> [4, 6, 7]
If efficiency is important, you could use a set:
require 'set'
s = Set.new
a.reject { |e| s.add?(e) }
#=> [4, 6, 7]
You can use Array#select to return the elements where Array#count is greater than 1:
2.1.2 :005 > arr = [1,2,3,4,4,5,6,6,7,7]
=> [1, 2, 3, 4, 4, 5, 6, 6, 7, 7]
2.1.2 :006 > arr.select { |e| arr.count(e) > 1 }.uniq
=> [4, 6, 7]
Hope this helps

Resources