获取两个数组的最长公共前缀(从原始索引0开始的子数组)和后缀(以原始索引-1结尾的子数组)的最佳方法是什么?例如,给定两个数组:
[:foo, 1, :foo, 0, nil, :bar, "baz", false]
[:foo, 1, :foo, 0, true, :bar, false]
这些数组的最长公共前缀是:
[:foo, 1, :foo, 0]
这些数组的最长公共后缀是:
[false]
当索引0 / -1处的元素在原始数组中不同时,公共前缀/后缀应为空数组。
答案 0 :(得分:3)
一种可能的解决方案:
a1 = [:foo, 1, 0, nil, :bar, "baz", false]
a2 = [:foo, 1, 0, true, :bar, false]
a1.zip(a2).take_while { |x, y| x == y }.map(&:first)
#=> [:foo, 1, 0]
反转输入数组和输出数组会找到一个公共后缀:
a1.reverse.zip(a2.reverse).take_while { |x, y| x == y }.map(&:first).reverse
#=> [false]
边缘情况:zip
用“nil
值填充”参数“数组:
a1 = [true, nil, nil]
a2 = [true]
a1.zip(a2).take_while { |x, y| x == y }.map(&:first)
#=> [true, nil, nil]
可以通过将初始数组截断为第二个数组的长度来避免这种情况:
a1[0...a2.size].zip(a2).take_while { |x, y| x == y }.map(&:first)
答案 1 :(得分:2)
没有边缘情况的另一种解决方案:
a1 = [:foo, 1, 0, nil, :bar, "baz", false]
a2 = [:foo, 1, 0, true, :bar, false]
a1.take_while.with_index {|v,i| a2.size > i && v == a2[i] }
编辑:提高绩效
class Array
def common_prefix_with(other)
other_size = other.size
take_while.with_index {|v,i| other_size > i && v == other[i] }
end
end
a1.common_prefix_with a2
答案 2 :(得分:1)
三种解决方案:野蛮(#3,初步回答),更好(#2,第一次编辑)和最佳(#1,@ Stefan的答案,第二次编辑)。
a = [:foo, 1, :foo, 0, nil, :bar, "baz", false]
b = [:foo, 1, :foo, 0, true, "baz", false]
c = [:foo,1,:goo]
d = [:goo,1,:new]
注意{OPO}示例稍稍更改了b
。
除非下面另有说明,否则将通过反转数组计算公共后缀,应用common_prefix
然后反转结果。
<强>#1 强>
Stefan的答案的变体将zip
和map
排除在外(并保留了将一个数组截断到最多其他数组长度的技巧):
def common_prefix(a,b)
a[0,b.size].take_while.with_index { |e,i| e == b[i] }
end
common_prefix(a,b)
#=> [:foo, 1, :foo, 0]
common_prefix(c,d)
#=> []
<强>#2 强>
def common_prefix(a,b)
any, arr = a.zip(b).chunk { |e,f| e==f }.first
any ? arr.map(&:first) : []
end
def common_suffix(a,b)
any, arr = a[a.size-b.size..-1].zip(b).chunk { |e,f| e==f }.to_a.last
any ? arr.map(&:first) : []
end
common_prefix(a,b)
#=> [:foo, 1, :foo, 0]
# Nore: any, arr = a.zip(b).chunk { |e,f| e==f }.to_a
# => [[true, [[:foo, :foo], [1, 1], [:foo, :foo], [0, 0]]],
# [false, [[nil, true], [:bar, :baz], ["baz", false], [false, nil]]]]
common_suffix(a,b)
#=> ["baz", false]
# Note: any, arr = a[a.size-b.size..-1].zip(b).chunk { |e,f| e==f }.to_a
# => [[false, [[1, :foo], [:foo, 1], [0, :foo], [nil, 0], [:bar, true]]],
# [true, [["baz", "baz"], [false, false]]]]
当:first
被发送到枚举器Enumerable#chunk时,将返回枚举器的第一个元素。因此,它的效率应与使用Enumerable#take_while相当。
common_prefix(c,d)
#=> []
common_suffix(c,d)
#=> []
<强>#3 强>
def common_prefix(a,b)
a[0,(0..[a.size, b.size].min).max_by { |n| (a[0,n]==b[0,n]) ? n : -1 }]
end
common_prefix(a,b)
#=> [:foo, 1, :foo, 0]
common_prefix(c,d)
#=> []
答案 3 :(得分:1)
先生们,启动引擎!
测试方法
我测试了@stefan给出的第二种方法,@ BroiSatse提出的两种方法以及我提供的三种方法。
class Array #for broiSatse2
def common_prefix_with(other)
other_size = other.size
take_while.with_index {|v,i| other_size > i && v == other[i] }
end
end
class Cars
def self.stefan(a1,a2)
a1[0...a2.size].zip(a2).take_while { |x, y| x == y }.map(&:first)
end
def self.broiSatse1(a1,a2)
a1.take_while.with_index {|v,i| a2.size > i && v == a2[i] }
end
def self.broiSatse2(a1,a2)
a1.common_prefix_with(a2)
end
def self.cary1(a,b)
a[0,b.size].take_while.with_index { |e,i| e == b[i] }
end
def self.cary2(a,b)
any, arr = a.zip(b).chunk { |e,f| e==f }.first
any ? arr.map(&:first) : []
end
def self.cary3(a,b)
a[0,(0..[a.size, b.size].min).max_by { |n| (a[0,n]==b[0,n]) ? n : -1 }]
end
end
我没有包含@ 6ftDan给出的解决方案(不要与5&#39; Dan或7&#39; Dan混淆),因为它没有通过所有测试。
构建测试数组
random_arrays(n)
构造一对数组。 n
是两者中较小者的大小。较大的是n+1
。{/ p>
def random_arrays(n)
m = rand(n)
# make arrays the same for the first m elements
a = Array.new(m,0)
b = Array.new(m,0)
if m < n
# make the m+1 elements different
a << 0
b << 1
# randomly assign 0s and 1a to the remaining elements
(n-m-1).times { a << rand(2); b << rand(2) } if m < n - 1
end
# make arrays unequal in length
(rand(2) == 0) ? a << 0 : b << 0
[a,b]
end
确认测试方法会产生相同的结果
N = 10000 #size of smaller of two input arrays
methods = Cars.methods(false)
# ensure are methods produce the same results and that
# all deal with edge cases properly
20.times do |i|
test = case i
when 0 then [[0,1],[1,1]]
when 1 then [[0],[]]
when 1 then [[0,0,0],[0,0]]
else
random_arrays(N)
end
soln = Cars.send(methods.first, *test)
methods[1..-1].each do |m|
unless soln == Cars.send(m, *test)
puts "#{m} has incorrect solution for #{test}"
exit
end
end
end
puts "All methods yield the same answers\n"
<强>基准强>
require 'benchmark'
I = 1000 #number of array pairs to test
@arr = I.times.with_object([]) { |_,a| a << random_arrays(N) } #test arrays
#test method m
def testit(m)
I.times { |i| Cars.send(m, *@arr[i]) }
end
Benchmark.bm(12) { |bm| methods.each { |m| bm.report(m) { testit(m) } } end
<强>结果
All methods yield the same answers
user system total real
stefan 11.260000 0.050000 11.310000 ( 11.351626)
broiSatse1 0.860000 0.000000 0.860000 ( 0.872256)
broiSatse2 0.720000 0.010000 0.730000 ( 0.717797)
cary1 0.680000 0.000000 0.680000 ( 0.684847)
cary2 13.130000 0.040000 13.170000 ( 13.215581)
cary3 51.840000 0.120000 51.960000 ( 52.188477)
答案 4 :(得分:0)
这适用于唯一数组。
x = [:foo, 1, 0, nil, :bar, "baz", false]
y = [:foo, 1, 0, true, :bar, false]
(x & y).select.with_index {|item,index| x.index(item) == index}
输出:=> [:foo, 1, 0]
(x.reverse & y.reverse).select.with_index {|item,index| x.reverse.index(item) == index}
输出:=> [false]