我有一个数组数组。在每个子数组中,如果两个或多个元素共享长度等于或大于8的前缀,那么我想用它们最长的前缀替换这些元素。对于这个数组:
m = [
["A", "97455589955", "97455589920", "97455589921"],
["B", "2348045101518", "2348090001559"]
]
我期待这样的输出:
n = [
["A", "974555899"],
["B", "2348045101518", "2348090001559"]
]
对于m
中的第一个子数组,最长的前缀是长度为9的"974555899"
。
974555899-55
974555899-20
974555899-21
对于第二个子阵列,最长的前缀是长度为5的"23480"
,并且短于8。在这种情况下,第二个子阵列保持不变。
23480-45101518
23480-90001559
对于此输入:
m = [
["A", "2491250873330", "249111222333", "2491250872214", "2491250872213"],
["B", "221709900000"],
["C", "6590247968", "6590247969", "6598540040", "65985400217"]
]
输出应该是这样的:
[
["A", "2491250873330", "249111222333", "249125087221"],
["B", "221709900000"],
["C", "659024796", "65985400"]
]
对于array m[0]
,其四个数字之间没有足够长的前缀,但在249125087221
和m[0][2]
之间有一个长度为12的前缀m[0][3]
。对于array m[2]
,"659024796"
和m[2][0]
之间的长度为9的前缀m[2][1]
,"65985400"
之间还有另一个长度为8的前缀m[2][2]
和m[2][3]
。
我构建了以下代码:
m.map{|x, *y|
[x, y.map{|z| z[0..7]}.uniq].flatten
}
我的代码带有第一个输入,我得到了这个输出。
[
["A", "97455589"],
["B", "23480451", "23480900"]
]
我坚持如何在不设置固定长度的情况下动态获取公共前缀。
答案 0 :(得分:1)
<强>代码强>
def doit(arr, min_common_length)
arr.map do |label, *values|
[label, values.group_by { |s| s[0, min_common_length] }.
map { |_,a| a.first[0, nbr_common_digits(a, min_common_length)] }]
end
end
def nbr_common_digits(a, min_common_length)
max_digits = a.map(&:size).min
return max_digits if max_digits == min_common_length + 1
(min_common_length..max_digits).find { |i|
a.map { |s| s[i] }.uniq.size > 1 } || max_digits
end
示例强>
arr = [["A","2491250873330","249111222333","2491250872214","2491250872213"],
["B","221709900000"],
["C","6590247968","6590247969","6598540040","65985400217"]]
doit(arr, 8)
#=> [["A", ["249125087", "249111222333"]],
# ["B", ["221709900000"]],
# ["C", ["659024796", "65985400"]]]
<强>解释强>
让我们首先考虑辅助方法nbr_common_digits
。假设
a = ["123467", "12345", "1234789"]
min_common_length = 2
然后步骤如下。
max_digits = a.map(&:size).min
#=> 5 (the length of "12345")
max_digits == min_common_length + 1
#=> 5 == 2 + 1
#=> false, so do not return max_digits
b = (min_common_length..max_digits).find { |i| a.map { |s| s[i] }.uniq.size > 1 }
#=> (2..5).find { |i| a.map { |s| s[i] }.uniq.size > 1 }
#=> 4
此时我们必须考虑b
将等于nil
的可能性,这种情况发生在所有字符串的第一个5
字符相等时。在这种情况下,我们应该返回max_digits
,这就是我们需要以下内容的原因。
b || max_digits
#=> 4
在doit
中,步骤如下。
min_common_length = 8
首先,我们使用Enumerable#group_by按照第一个min_common_length
数字对值进行分组。
arr.map { |label, *values| [label,
values.group_by { |s| s[0, min_common_length] }] }
#=> [["A", {"24912508"=>["2491250873330", "2491250872214", "2491250872213"],
# "24911122"=>["249111222333"]}],
# ["B", {"22170990"=>["221709900000"]}],
# ["C", {"65902479"=>["6590247968", "6590247969"],
# "65985400"=>["6598540040", "65985400217"]}]]
第二步是计算最长的公共长度并根据需要替换值。
arr.map do |label, *values| [label,
values.group_by { |s| s[0, min_common_length] }.
map { |_,a| a.first[0, nbr_common_digits(a, min_common_length)] }]
end
#=> [["A", ["249125087", "249111222333"]],
# ["B", ["221709900000"]],
# ["C", ["659024796", "65985400"]]]
第二个map
块中的第一个块变量(其值等于带有nbr_common_length
个字符的字符串 - group_by
的分组标准)由下划线表示(a合法的局部变量)表示它不用于块计算。
答案 1 :(得分:1)
这是一个有趣的问题。这是我的解决方案:
def lcn(lim, *arr)
# compute all substrings of lengths >= lim and build a lookup by length
lookup = lcn_explode(lim, arr)
# first pass: look for largest common number among all elements
res, = lcn_filter(arr, lookup) { |size| size == arr.size }
return res unless res.empty?
# second pass: look for largest common number among some elements
res, rem = lcn_filter(arr, lookup) { |size| size > 1 }
# append remaining candidates with no matches
res.concat(rem)
end
def lcn_explode(lim, arr)
memo = Hash.new { |h, k| h[k] = Array.new }
arr.uniq.each do |n|
lim.upto([n.size, lim].max) do |i|
memo[i] << [n[0, i], n]
end
end
memo
end
def lcn_filter(arr, lookup)
memo = []
lookup.keys.sort!.reverse_each do |i|
break if arr.empty?
matches = Hash.new { |h, k| h[k] = Array.new }
lookup[i].each do |m, n|
matches[m] << n if arr.include?(n)
end
matches.each_pair do |m, v|
next unless yield v.size
memo << m
# remove elements from input array so they won't be reused
arr -= v
end
end
return memo, arr
end
你这样使用它:
p lcn(8, "97455589955", "97455589920", "97455589921") => ["974555899"]
或者:
m.each do |key, *arr|
p [key, *lcn(8, *arr)]
end
打印哪些:
["A", "249125087221", "2491250873330", "249111222333"]
["B", "221709900000"]
["C", "659024796", "65985400"]
答案 2 :(得分:0)
您的任务可以分为两部分:计算最大公共号码和修改原始数组。
最大公共号对数组进行操作,因此它应该是Array的方法。
计算LCN后,您可以将其长度与限制进行比较(即8)。
class Array
def lcn
first.length.times do |index|
numb = first[0..index]
return numb unless self[1..-1].all? { |n| n.start_with?(numb) }
end
first
end
end
def task(m, limit = 8)
m.map { |i,*n| [i, n.lcn.length >= limit ? n.lcn : n].flatten }
end
task(m) # => [["A", "9745558995"], ["B", "2348045101518", "2348090001559"]]
在您的解决方案中,您实际上并未实现lcn查找和过滤输出。