Finding a Perfect substring in a string in ruby [closed] - ruby

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
A String s comprised of numbers from 0-9 contains a perfect substring if all the elements within a substring occurs exactly k times. Calculate the number of perfect substrings in s.
EXAMPLE
s = '1102021222'
k = 2
Here s contains 6 substrings:
11
0202
110202
102021
22
22
Can anyone help me with the solution to this in Ruby with the least complexity?
TIA..

Code
def count_em(str, k)
enum = str.each_char
(k..str.size).step(k).sum do |n|
enum.each_cons(n).count do |a|
a.tally.all? { |_k,v| v == k }
end
end
end
Examples
str = '1102021222'
count_em(str, 1) #=> 18 ( 1, 1, 0, 2, 0, 2, 1, 2, 2, 2,
# 10, 02, 20, 02, 21, 12, 102, 021
count_em(str, 2) #=> 6 (11, 22, 22, 0202, 110202, 102021)
count_em(str, 3) #=> 1 (102021)
count_em(str, n) #=> 0 for n > 3
Explanation
Enumerable#tally made its debut in Ruby v2.7. To support earlier versions of Ruby replace tally with
each_with_object(Hash.new(0)) { |n,h| h[n] += 1 }
Enumerable#sum was new in Ruby 2.4. To support earlier versions of Ruby replace sum with
reduce(:+)
See also Enumerable#each_cons, Enumerable#count and Enumerable#all? and Hash::new.
Note that no strings having k instances of all unique digits contained in the string could be produced for values of n that are not multiples of k. It is for that reason that step(k) is present.
The easiest way to explain the calculations is to salt the method with puts statements and run it.
def count_em(str, k)
puts "k = #{k}"
enum0 = str.each_char
puts "enum0 = #{enum0}"
(k..str.size).step(k).sum do |n|
puts "n = #{n}"
enum1 = enum0.each_cons(n)
puts " enum1.to_a = #{enum1.to_a}"
enum1.count do |a|
puts " a = #{a}"
h = a.tally
puts " a.tally = #{h}"
puts " a.tally.all? {|_k,v| v == k} = #{h.all? {|_k,v| v==k}}"
h.all? { |_k,v| v==k }
end
end
end
The line puts " enum1.to_a = #{enum1.to_a}" merely displays the values that will be generated by the enumerator enum1 and passed to count's block.
Try it for k = 2:
count_em(str, 2)
#=> 6
displays the following:
k = 2
enum0 = #<Enumerator:0x00007f9467ab60f0>
n = 2
enum1.to_a = [["1", "1"], ["1", "0"], ["0", "2"], ["2", "0"],
["0", "2"], ["2", "1"], ["1", "2"], ["2", "2"],
["2", "2"]]
a = ["1", "1"]
a.tally = {"1"=>2}
a.tally.all? {|_k,v| v == k} = true
a = ["1", "0"]
a.tally = {"1"=>1, "0"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["0", "2"]
a.tally = {"0"=>1, "2"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["2", "0"]
a.tally = {"2"=>1, "0"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["0", "2"]
a.tally = {"0"=>1, "2"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["2", "1"]
a.tally = {"2"=>1, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["1", "2"]
a.tally = {"1"=>1, "2"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["2", "2"]
a.tally = {"2"=>2}
a.tally.all? {|_k,v| v == k} = true
a = ["2", "2"]
a.tally = {"2"=>2}
a.tally.all? {|_k,v| v == k} = true
n = 4
enum1.to_a = [["1", "1", "0", "2"], ["1", "0", "2", "0"],
["0", "2", "0", "2"], ["2", "0", "2", "1"],
["0", "2", "1", "2"], ["2", "1", "2", "2"],
["1", "2", "2", "2"]]
a = ["1", "1", "0", "2"]
a.tally = {"1"=>2, "0"=>1, "2"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["1", "0", "2", "0"]
a.tally = {"1"=>1, "0"=>2, "2"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["0", "2", "0", "2"]
a.tally = {"0"=>2, "2"=>2}
a.tally.all? {|_k,v| v == k} = true
a = ["2", "0", "2", "1"]
a.tally = {"2"=>2, "0"=>1, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["0", "2", "1", "2"]
a.tally = {"0"=>1, "2"=>2, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["2", "1", "2", "2"]
a.tally = {"2"=>3, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["1", "2", "2", "2"]
a.tally = {"1"=>1, "2"=>3}
a.tally.all? {|_k,v| v == k} = false
n = 6
enum1.to_a = [["1", "1", "0", "2", "0", "2"], ["1", "0", "2", "0", "2", "1"],
["0", "2", "0", "2", "1", "2"], ["2", "0", "2", "1", "2", "2"],
["0", "2", "1", "2", "2", "2"]]
a = ["1", "1", "0", "2", "0", "2"]
a.tally = {"1"=>2, "0"=>2, "2"=>2}
a.tally.all? {|_k,v| v == k} = true
a = ["1", "0", "2", "0", "2", "1"]
a.tally = {"1"=>2, "0"=>2, "2"=>2}
a.tally.all? {|_k,v| v == k} = true
a = ["0", "2", "0", "2", "1", "2"]
a.tally = {"0"=>2, "2"=>3, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["2", "0", "2", "1", "2", "2"]
a.tally = {"2"=>4, "0"=>1, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
a = ["0", "2", "1", "2", "2", "2"]
a.tally = {"0"=>1, "2"=>4, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
n = 8
enum1.to_a = [["1", "1", "0", "2", "0", "2", "1", "2"],
["1", "0", "2", "0", "2", "1", "2", "2"],
["0", "2", "0", "2", "1", "2", "2", "2"]]
a = ["1", "1", "0", "2", "0", "2", "1", "2"]
a.tally = {"1"=>3, "0"=>2, "2"=>3}
a.tally.all? {|_k,v| v == k} = false
a = ["1", "0", "2", "0", "2", "1", "2", "2"]
a.tally = {"1"=>2, "0"=>2, "2"=>4}
a.tally.all? {|_k,v| v == k} = false
a = ["0", "2", "0", "2", "1", "2", "2", "2"]
a.tally = {"0"=>2, "2"=>5, "1"=>1}
a.tally.all? {|_k,v| v == k} = false
n = 10
enum1.to_a = [["1", "1", "0", "2", "0", "2", "1", "2", "2", "2"]]
a = ["1", "1", "0", "2", "0", "2", "1", "2", "2", "2"]
a.tally = {"1"=>3, "0"=>2, "2"=>5}
a.tally.all? {|_k,v| v == k} = false

Related

Can we do below problem by initializing hash's key with a default value?

Write a method that groups the above hash into 2 groups of 'even' and 'odd' length using 'inject'.
input - "['abc','def','1234','234','abcd','x','mnop','5','zZzZ']"
My code listed below already works. But I want to know better way to do it using default value for hash's key. I meant to say something like below -
h=Hash.new { |hash, key| hash[key] = []}
Solution :
class Array
def group_even_odd
key_hash = group_by(&:length)
key_array = %w(even odd)
key_hash.each_with_object('odd' => [], 'even' => []) do |(key, value), even_odd_hash|
even_odd_hash[key_array[key % 2]].push(value)
even_odd_hash
end
end
end
if ARGV.empty?
puts 'Please provide an input'
else
input = ARGV[0].scan(/\w+/).map(&:to_s)
puts input.group_even_odd
end
Expected and actual are same, code is working.
Expected result -
{"odd"=>[["abc", "def", "234"], ["x", "5"]], "even"=>[["1234", "abcd", "mnop", "zZzZ"]]}
One possible option, given
ary = ["abc", "def", "1234", "234", "abcd", "x", "mnop", "5", "zZzZ"]
First group by even odd, then group by size:
ary.group_by { |e| e.size.even? ? 'even' : 'odd' }
.transform_values { |v| v.group_by(&:size).values }
#= {"odd"=>[["abc", "def", "234"], ["x", "5"]], "even"=>[["1234", "abcd", "mnop", "zZzZ"]]}
First step, to explain:
ary.group_by { |e| e.size.even? ? 'even' : 'odd' }
#=> {"odd"=>["abc", "def", "234", "x", "5"], "even"=>["1234", "abcd", "mnop", "zZzZ"]}
Then Hash#transform_values grouping each by size.
The following does not meet the requirement that inject (aka reduce) be used, but it is how I would do it.
arr = ['abc', 'def', '1234', '234', 'abcd', 'x', 'mnop', '5', 'zZzZ']
odd, even = arr.each_with_object(Hash.new { |h,k| h[k]=[] }) do |s,h|
h[s.size] << s
end.
values.
partition { |a| a.first.size.odd? }
#=> [[["abc", "def", "234"], ["x", "5"]],
# [["1234", "abcd", "mnop", "zZzZ"]]]
{ "odd"=>odd, "even"=>even }
#=> {"odd"=>[["abc", "def", "234"], ["x", "5"]],
# "even"=>[["1234", "abcd", "mnop", "zZzZ"]]}
The steps are as follows.
h = arr.each_with_object(Hash.new {|h,k| h[k]=[]}) do |s,h|
h[s.size] << s
end
#=> {3=>["abc", "def", "234"], 4=>["1234", "abcd", "mnop", "zZzZ"],
# 1=>["x", "5"]}
a = h.values
#=> [["abc", "def", "234"], ["1234", "abcd", "mnop", "zZzZ"],
# ["x", "5"]]
odd, even = a.partition { |a| a.first.size.odd? }
#=> [[["abc", "def", "234"], ["x", "5"]],
# [["1234", "abcd", "mnop", "zZzZ"]]]
{ "odd"=>odd, "even"=>even }
#=> {"odd"=>[["abc", "def", "234"], ["x", "5"]],
# "even"=>[["1234", "abcd", "mnop", "zZzZ"]]}
If one insists on fitting a square peg into a round hold (using inject/reduce), I suppose that could be done as follows.
arr.reduce({ "odd"=>[], "even"=>[] }) do |g,s|
oe = s.size.odd? ? "odd" : "even"
i = g[oe].find_index { |a| a.any? && a.first.size == s.size }
case i.nil?
when true then g[oe] << [s]
else g[oe][i] << s
end
g
end
#=> {"odd"=>[["abc", "def", "234"], ["x", "5"]],
# "even"=>[["1234", "abcd", "mnop", "zZzZ"]]}

How to find if one array is a subset of another array in Ruby?

Preferably in Ruby
I need a way to determine if one array is a "subarray" of another array when order matters
For instance,
a = ["1", "4", "5", "7", "10"]
b = ["1", "4", "5"]
c = ["1", "5", "4"]
d = ["1", "5", "10"]
a includes b = true
a includes c = false
a include d = false
Thanks in advance.
[b, c, d].map do |arr|
a.each_cons(arr.length).any?(&arr.method(:==))
end
#⇒ [true, false, false]
This is definitely not the most performant solution, but for not huge arrays is works and, which is important, is readable.
Enumerable#each_cons.
a = ["1", "4", "5", "7", "10"]
b = ["1", "4", "5"]
c = ["1", "5", "4"]
d = ["1", "5", "10"]
def sub_set?(arr_a, arr_b)
arr_a.select.with_index do |a, index|
arr_b[index] == a
end == arr_b
end
puts "testing sub_set #{a.inspect} and #{c.inspect}"
puts sub_set?(a,c).inspect
class Array
def includes(array)
return false if array.size > self.size
indexes = []
self.each_with_index {|e, i| indexes << i if e == array[0]}
p indexes
indexes.each do |i|
return true if self[i..i+array.size-1] == array
end
return false
end
end
p a.includes b
p a.includes c
p a.includes d
Or less phpish :)
class Array
def includes(array)
return false if array.size > self.size
indexes = each.with_index.select { |e, i| e == array[0] }.map(&:last)
indexes.each {|i| self[i..i+array.size-1] == array ? (return true) : (return false)}
end
end
p a.includes b
p a.includes c
p a.includes d
You could join the elements with an arbitrary character then compare strings:
a.join(' ').match?(array.join(' '))
This works for the test cases you have provided:
a.join(' ').match?(b.join(' '))
#=> true
a.join(' ').match?(c.join(' '))
#=> false
a.join(' ').match?(d.join(' '))
#=> false
But this is not a general solution and will fail on varying types of arrays (see comments for further discussion).

How to implement order of operations for an expression in an array?

Given the expression
1 * 2 / 3 + 4
I'm trying to write out all possible variants of the equation when implementing order of operations. Such as:
(1 * 2) / 3) + 4
and
(1 * (2 / 3)) + 4
This is what I have now:
expression = [1.0, "+", 2.0, "+", 3.0, "+", 4.0]
storage = []
exp1 = expression #Make new object because insert method will overwrite expression object
storage << exp1.insert(0, "((").insert(4, ")").insert(7, ")")
But exp1's changes reflect in expression as well. I even made them in separate methods but the changes keep reflecting on expression.
How do I keep the expression object unchanged? I need to configure the original expression 5 times in total, but I'm struggling to keep it clean when I use insert. Reading the Ruby docs, the insert method is the only one suitable to what I'm doing.
Do I have to use a different data structure to store 1 * 2 / 3 + 4 other than an array?
Consider using #dup so that you don't alter the original set:
expression.dup.insert(0, "((").insert(4, ")").insert(7, ")")
Code
def all_equations(expression)
nums = expression.select { |o| o.kind_of? Numeric }.map(&:to_s)
ops = expression.select { |o| o.is_a? String }.uniq
ops.permutation(ops.size).to_a.map { |ao|
([nums.first] + ao.zip(nums[1..-1]).map(&:join)).join }
end
Examples
all_equations [1.0, "*", 2.0, "/", 3.0, "+", 4.0]
#=> ["1.0*2.0/3.0+4.0", "1.0*2.0+3.0/4.0", "1.0/2.0*3.0+4.0",
# "1.0/2.0+3.0*4.0", "1.0+2.0*3.0/4.0", "1.0+2.0/3.0*4.0"]
a = all_equations [1.0, "*", 2.0, "/", 3.0, "+", 4.0, "-", 4.0, "**", 5.0]
#=> ["1.0*2.0/3.0+4.0-4.0**5.0", "1.0*2.0/3.0+4.0**4.0-5.0",
# ...
# "1.0*2.0+3.0**4.0/4.0-5.0", "1.0*2.0+3.0**4.0-4.0/5.0",
# ...
# "1.0**2.0-3.0+4.0*4.0/5.0", "1.0**2.0-3.0+4.0/4.0*5.0"]
a.size
#=> 120
Explanation
The steps are as follows.
expression = [1.0, "*", 2.0, "/", 3.0, "*", 3.0] # note `"*"` appears twice
nums = expression.select { |o| o.kind_of? Numeric }.map(&:to_s)
#=> ["1.0", "2.0", "3.0", "3.0"]
ops = expression.select { |o| o.is_a? String }
#=> ["*", "/", "*"]
a = ops.uniq
#=> ["*", "/"]
b = a.permutation(a.size)
#=> #<Enumerator: ["*", "/"]:permutation(2)>
c = b.to_a
#=> [["*", "/"], ["/", "*"]]
c.map { |ao| ([nums.first] + ao.zip(nums[1..-1]).map(&:join)).join }
#=> ["1.0*2.0/3.0", "1.0/2.0*3.0"]
uniq has effect only when ops contains duplicates.
Extension
The following permits the numbers to be reordered as well. (I initially thought this was a requirement, which is the only reason I am offering this modification.)
expression = [1.0, "*", 2.0, "/", 3.0, "+", 4.0]
nums = expression.select { |o| o.kind_of? Numeric }.map(&:to_s)
#=> ["1.0", "2.0", "3.0", "4.0"]
ops = expression.select { |o| o.is_a? String }
#=> [["*", "/", "+"], ["*", "+", "/"], ["/", "*", "+"],
# ["/", "+", "*"], ["+", "*", "/"], ["+", "/", "*"]]
anums, aops = nums.permutation(nums.size).to_a.uniq, ops.permutation(ops.size).to_a.uniq
anums
#=> [["1", "2", "3", "4"], ["1", "2", "4", "3"], ["1", "3", "2", "4"],
# ["1", "3", "4", "2"], ["1", "4", "2", "3"], ["1", "4", "3", "2"],
# ["2", "1", "3", "4"], ["2", "1", "4", "3"], ["2", "3", "1", "4"],
# ["2", "3", "4", "1"], ["2", "4", "1", "3"], ["2", "4", "3", "1"],
# ["3", "1", "2", "4"], ["3", "1", "4", "2"], ["3", "2", "1", "4"],
# ["3", "2", "4", "1"], ["3", "4", "1", "2"], ["3", "4", "2", "1"],
# ["4", "1", "2", "3"], ["4", "1", "3", "2"], ["4", "2", "1", "3"],
# ["4", "2", "3", "1"], ["4", "3", "1", "2"], ["4", "3", "2", "1"]]
aops
#=> [["*", "/", "+"], ["*", "+", "/"], ["/", "*", "+"],
# ["/", "+", "*"], ["+", "*", "/"], ["+", "/", "*"]]
a = anums.product(aops).map { |an,ao|
([an.first] + ao.zip(an[1..-1]).map(&:join)).join }
#=> ["1*2/3+4", "1*2+3/4", "1/2*3+4",..., "4/3+2*1", "4+3*2/1", "4+3/2*1"]
a.size
#=> 144

MapReduce arrays in Ruby

I have two arrays like this:
["1","7","8","10"]
and
["1","2","3","6","9","11"]
These arrays represents ids from a class called Place that a user selected. I want to select the places ids with most votes. I tried transpose but as the arrays have different sizes, they cannot be transposed.
The expected output for this example is:
{ "1" => 2, "7" => 1, "8" => 1, "10" => 1, "2" => 1, "3" => 1, "6" => 1, "9" => 1, "11" => 1 }
You can join all arrays and calculate the number of identical elements like this:
arrays = [["1","7","8","10"], ["1","2","3","6","9","11"]].reduce(:+)
arrays.inject(Hash.new(0)) { |memo, e| memo.update(e => memo[e] + 1) }
# "{ "1" => 2, "7" => 1, "8" => 1, "10" => 1, "2" => 1, "3" => 1, "6" => 1, "9" => 1, "11" => 1 }"
Once you have this intermediate result use max_by to select the key with the max value from the hash:
arrays = [["1","7","8","10"], ["1","2","3","6","9","11"]].reduce(:+)
arrays.inject(Hash.new(0)) { |memo, e| memo.update(e => memo[e] + 1) }
.max_by { |_, count| count }[0]
#=> "1"
This is another way:
arr = [["1","7","8","10"], ["1","2","3","6","9","11"], ["1","2","7"]]
h = arr.flatten.sort_by(&:to_i).group_by(&:itself)
h.update(h) { |_,v| v.size }
#=> {"1"=>3, "2"=>2, "3"=>1, "6"=>1, "7"=>2, "8"=>1, "9"=>1, "10"=>1, "11"=>1}
The steps:
a = arr.flatten
#=> ["1", "7", "8", "10", "1", "2", "3", "6", "9", "11", "1", "2", "7"]
b = a.sort_by(&:to_i)
#=> ["1", "1", "1", "2", "2", "3", "6", "7", "7", "8", "9", "10", "11"]
h = b.group_by(&:itself)
#=> {"1"=>["1", "1", "1"], "2"=>["2", "2"], "3"=>["3"], "6"=>["6"],
# "7"=>["7", "7"], "8"=>["8"], "9"=>["9"], "10"=>["10"], "11"=>["11"]}
If you are using a version of Ruby prior to 2.2 (when Object#itself was introduced) you will need to instead write:
h = b.group_by { |s| s }
Lastly:
h.update(h) { |_,v| v.size }
#=> {"1"=>["1", "1", "1"], "2"=>["2", "2"], "3"=>["3"], "6"=>["6"],
# "7"=>["7", "7"], "8"=>["8"], "9"=>["9"], "10"=>["10"], "11"=>["11"]}
This uses the form of Hash#update (aka merge!) that employs a block (here { |_,v| v.size }) to determine the values of keys that are present in both hashes being merged (which in this case is all of the keys).
Update: the method Hash#transform_values made its debut in Ruby v2.4. This allows us to write the following.
arr.flatten.
sort_by(&:to_i).
group_by(&:itself).
transform_values(&:size)

Generate alphanumeric sequence in Ruby

How would I go about generating a sequential alphanumeric string?
Each string should only be 8 characters.
The characters possible for each position are:
["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "B", "C", "D", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "V", "W", "X", "Y", "Z"]
Also, If possible I would like to choose the starting point of the sequence.
For example:
00000001
00000002
00000003
00000005
...
0000L3FH
0000L3FJ
0000L3FK
0000L3FL
0000L3FM
0000L3FN
0000L3FP
...
0000L4FP
0000L4FQ
0000L4FR
0000L4FS
...
0000M000
0000M001
0000M002
That is a permutation with repetition. Arrays can do that out of the box.
chars = %w(0 1 2 B C)
sequencer = chars.repeated_permutation(chars.size) #OP:replace chars.size by 8
10.times{p sequencer.next}
#["0", "0", "0", "0", "0"]
#["0", "0", "0", "0", "1"]
#["0", "0", "0", "0", "2"]
#["0", "0", "0", "0", "B"]
#["0", "0", "0", "0", "C"]
#["0", "0", "0", "1", "0"]
#["0", "0", "0", "1", "1"]
#["0", "0", "0", "1", "2"]
#["0", "0", "0", "1", "B"]
#["0", "0", "0", "1", "C"]
p sequencer.next
#["0", "0", "0", "2", "0"]
This one allows for setting the starting point:
dial = %w(0 1 2 A B)
start_position = %w(A B 0) #for instance.
p clock = start_position.map{|char| dial.rotate(dial.index(char))}
#[["A","B","0","1","2"], ["B","0","1","2","A"], ["0","1","2","A","B"]]
# start ticking:
clock.shift.product(*clock){|tick|p tick}
#["A", "B", "0"]
#["A", "B", "1"]
#["A", "B", "2"]
#["A", "B", "A"]
#["A", "B", "B"]
#["A", "0", "0"]
#...

Resources