Code
def doit(arr)
a, nbr = arr.each_cons(2).map do |s1,s2|
n = [s1.size, s2.size].min.downto(1).find { |n| s1[-n..-1] == s2[0,n] }
n.nil? ? [s2, 0] : [s2[n..-1],n]
end.transpose
[a.unshift(arr.first).join, nbr.min]
end
Examples
doit ["oven", "envier", "erase", "serious"]
#=> ["ovenvieraserious", 2]
doit ["move", "over", "very"]
# ["movery", 3]
doit ["to", "ops", "psy", "syllable"]
#["topsyllable", 1]
Explanation
The steps are as follows.
arr = ["oven", "envier", "erase", "serious"]
e = arr.each_cons(2)
#=> #<Enumerator: ["oven", "envier", "erase", "serious"]:each_cons(2)>
see Enumerable#each_cons. We can see the elements that will be generated by this enumerator and passed to map by converting it to an array.
e.to_a
#=> [["oven", "envier"], ["envier", "erase"], ["erase", "serious"]]
Continuing,
b = e.map do |s1,s2|
n = [s1.size, s2.size].min.downto(1).find {|n| s1[-n..-1] == s2[0,n]}
n.nil? ? [s2, 0] : [s2[n..-1],n]
end
#=> [["vier", 2], ["ase", 2], ["rious", 2]]
a, nbr = b.transpose
#=> [["vier", "ase", "rious"], [2, 2, 2]]
a #=> ["vier", "ase", "rious"]
nbr
#=> [2, 2, 2]
Computing a and nbr from b.transpose makes use of
Array Decomposition. Continuing,
c = a.unshift(arr.first)
#=> ["oven", "vier", "ase", "rious"]
d = c.join
#=> "ovenvieraserious"
e = nbr.min
#=> 2
[d, e]
#=> ["ovenvieraserious", 2]
Consider now the calculation of b. The first element is generated by e and passed to map, which passes it to its block, and values are assigned to the block variables (again, with the help of array decomposition).
s1, s2 = e.next
#=> ["oven", "envier"]
s1
#=> "oven"
s2
#=> "envied"
The block calculation is now performed.
f = [s1.size, s2.size].min
#=> [4,6].min
#=> 4
g = f.downto(1).find { |n| s1[-n..-1] == s2[0,n] }
#=> 2
In computing g the block calculation preceeds as follows.
n = 4
s1[-n..-1] == s2[0,n]
#=> "oven" == "envi" => false
n = 3
s1[-n..-1] == s2[0,n]
#=> "ven" == "env" => false
n = 2
s1[-n..-1] == s2[0,n]
#=> "en" == "en" => true
This means that, when n #=> 2, the string consisting of the last n characters of s1 equals the first n characters of s2, and that is the largest value of n having that property.
Then
n.nil? ? [s2, 0] : [s2[n..-1],n]
#=> [s2[n..-1],n]
#=> ["envied"[2..-1],2]
#=> ["vied",2]
The next value of e is then generated, passed to map and the block variables are assigned new values:
s1, s2 = e.next
#=> ["oven", "envier"]
The block calculation is then performed, and so on.