比方说,我有两个单词数组:
array1 = ["hello", "world", "i", "am", "in", "the", "world"]
array2 = ["This", "is", "the", "hello", "world", "message"]
可以很容易地用两个字符串表示:
string1 = "hello world i am in the world"
string2 = "This is the hello world message"
让我们假设我现在要使用数组。
我想找到array2中最大的子数组,该数组以相同的顺序出现。
因此,如果您打算以可想象的最慢的方式进行操作,那么您会说:
但是,这感觉效率很低。谁能看到更好的方法?我正在使用Ruby,但是我对通用算法以及如何使用该特定语言感兴趣。
请注意,这不仅是数组的交集,因为(至少在ruby中)不关心元素的顺序,而我确实关心。
谢谢!
答案 0 :(得分:2)
直到我了解到“最长的公共子字符串/序列问题”(请参阅@Dustin的答案),我认为没有一种方法比您在问题中概述的方法更好:从最大的子数组开始({ {1}}本身),然后顺序将子数组的大小减小一个,直到找到匹配项为止(或者确定两个数组不包含公共元素)。尽管我现在看到了一种更有效的方法,但是您的想法肯定不是一个坏主意,尤其是在子字符串不太大的情况下,并且比Dustin引用的动态编程解决方案更容易实现。我已经在下面实现了您的想法。
我选择使用正则表达式来标识匹配项,因此我需要将array2
转换为字符串。
array1
计算如下。
str1 = array1.join(' ')
#=> "hello world i am in the world"
如果[array1.size, array2.size].min.downto(1).each do |n|
a = array2.each_cons(n).find { |a| str1.match?(/\b#{a.join(' ')}\b/) }
break a unless a.nil?
end
#=> ["hello", "world"]
和nil
没有公共元素,则返回 array1
。如果需要,可以先测试array2
。
这是对我上面的内容的可能改进。这个想法是尝试减少(array1 & array2).empty?
中的m
。
m.downto(1)
这在这里无济于事,但是如果数组h1 = array1.each_with_object(Hash.new(0)) { |word, h| h[word] += 1 }
#=> {"hello"=>1, "world"=>2, "i"=>1, "am"=>1, "in"=>1, "the"=>1}
h2 = array1.each_with_object(Hash.new(0)) { |word, h| h[word] += 1 }
#=> {"hello"=>1, "world"=>2, "i"=>1, "am"=>1, "in"=>1, "the"=>1}
m = [array1.size, array2.size, h2.sum { |k,v| [v, h2[k]].min }].min
#=> [7, 6, 7].min
#=> 6
和array1
不同则可能有用。
答案 1 :(得分:2)
这似乎是解决“最长公共子字符串”问题的方法,但是使用单词代替字符串中的字符。
此Wiki(https://en.wikipedia.org/wiki/Longest_common_substring_problem)概述了动态编程方法,用于在伪代码中定位最大的公共匹配项,并且可以将其移植到通过两个数组作为参数的红宝石上。
function LCSubstr(S[1..r], T[1..n])
L := array(1..r, 1..n)
z := 0
ret := {}
for i := 1..r
for j := 1..n
if S[i] == T[j]
if i == 1 or j == 1
L[i,j] := 1
else
L[i,j] := L[i-1,j-1] + 1
if L[i,j] > z
z := L[i,j]
ret := {S[i-z+1..i]}
else
if L[i,j] == z
ret := ret ∪ {S[i-z+1..i]}
else
L[i,j] := 0
return ret
答案 2 :(得分:2)
直接进行“六个字”测试 然后我遍历第二个数组中的每个单词,并测试它是否在第一个中。 如果是,则寻找它,然后寻找一个,如果两者都寻找,则寻找下一个。
即,一旦发现第一个数组中不存在“ This”,您还将丢弃其他五个以此开头的潜在候选对象。
答案 3 :(得分:1)
这是一个快速工作的解决方案,将比较结果缩减为仅适用于数组中共有的那些元素:
array1 = ["hello", "world", "i", "am", "in", "the", "world"]
array2 = ["This", "is", "the", "hello", "world", "message"]
common_words = array1 & array2
stringified_array1 = array1.join(' ')
stringified_array2 = array2.join(' ')
(common_words.length - 1).downto(0).map do |n|
stringified_combo = array1[0..n].join(' ')
if stringified_array1.include?(stringified_combo) && stringified_array2.include?(stringified_combo)
stringified_combo.split($,)
end
end.compact.max
这将使两个数组具有相同的词,并从大到小对它们进行测试。您检查它们在第一个数组中的顺序是否正确,然后在第二个数组中是否存在。
我很高兴能收到任何评论和反馈,但这种做法确实有效,并且有效
答案 4 :(得分:1)
这是Ruby中PHP实现的like_text的一部分。使用字符串:
def substrings(str)
(0...str.size).flat_map do |i|
(i...str.size).map { |j| str[i..j] }
end
end
def lcs(str1, str2)
(substrings(str1) & substrings(str2)).max_by(&:size)
end
puts "'#{lcs("hello world i am in the world", "This is the hello world message")}'"
=> 'hello world '
对子字符串的蛮力可能会成为Rust FFI调用的候选对象?我们并没有进行太大的比较,所以它对我们有用。