有效删除Ruby中数组中其他元素的所有子字符串

Posted

技术标签:

【中文标题】有效删除Ruby中数组中其他元素的所有子字符串【英文标题】:Efficient to delete all substrings of other elements within an array in Ruby 【发布时间】:2017-03-04 03:55:48 【问题描述】:

我有一个复杂的问题是手头的数组就地编辑。 我有一个数组,其中一些元素是其他元素的子字符串。我想删除所有子字符串并只保留超集/字符串。 即数组 => ['1', '1 1', '1 1 1', '1 1 1 2', '1 2 3 1', '1 2', '2 3'] 操作后我应该有一个经过消毒的数组 => ['1 1 1 2', '1 2 3 1']

有没有一种有效的算法来达到同样的效果?

【问题讨论】:

你能解释一下子字符串是什么意思吗? 史蒂夫给出了一个很好的答案,但以后请考虑在选择答案之前等待更长时间。快速回答会阻碍其他答案,并且可能会使仍在研究答案的人短路。许多人在这里等待至少几个小时。不用着急。 我同意@CarySwoveland @CarySwoveland 当然,我已经撤消了。我现在也在研究其中一种解决方案。很快就会发布。 如果 a = ["1","1","2","2"] 的预期输出是什么?您期望 [] 还是 ["1","2"]? 【参考方案1】:

这种方法使用一些数组数学来将自己从数组中删除,然后检查它是否显示为子字符串。我不知道它的性能如何。

a = ['1', '1 1', '1 1 1', '1 1 1 2', '1 2 3 1', '1 2', '2 3']
a.uniq.delete_if  |i| (a-[i]).any? |j| j.include? i  

我改为使用 delete_if,因为它会提高性能,因为只要发现子字符串就可以缩短数组,从而使后续检查稍微快一些。

更新:当数组包含重复项时,Cary Swoveland 发现了一个问题。我已经添加了一个 uniq 以首先对数组进行重复数据删除,尽管尚不完全清楚如果重复一个元素会发生什么,是否应该将它们都删除,因为它们是彼此的子字符串?我已经解决了这个问题,假设重复导致输出中只显示一个项目,但这可能是错误的。

【讨论】:

什么是a-[i]?我从没见过。 @eeeeeean 在控制台尝试一下 :) 基本上它是数组 - [数组元素] 它是一个内联数组,包含由 i 表示的元素。我正在通过获取现有数组并从中减去一个包含一个元素的数组来进行设置数学运算。这是我能想到的最简单的方法来删除一个元素,而不用担心它在列表中的显示位置。它还具有删除重复项的优点。在代码中,将其视为 ['1', '1 1', '1 1 1', '1 1 1 2', '1 2 3 1', '1 2', '2 3'] - [' 1'] 顺便说一句,非常好的解决方案 - 我喜欢它,+oned 抱歉泼冷水(我赞成),但是当a 包含重复项时会出现问题,因为a-[i] 会删除所有值为i 的元素。例如,a = ["1","1"]; a.delete_if |i| (a-[i]).any? |j| j.include? i #=> ["1", "1"]。那是因为a-[i] #=> []。解决方法是使用我已将 proposed 添加到 Ruby 核心的方法:a.delete_if |i| (a.difference([i]).any? |j| j.include? i 【参考方案2】:

这是一种在找到子字符串时删除它们的方法。

a = ['1', '1 1', '1 1 1', '1 1 1 2', '1 2 3 1', '1 2', '2 3']

b = a.dup
b.size.times do
  first, *rest = b
  (rest.any?  |t| t.include? first ) ? b.shift : b.rotate!
end
b #=> ["1 1 1 2", "1 2 3 1"]

要查看发生了什么,请插入

puts "first=\"#first\n, rest=#rest"

first,*rest = b 之后。打印以下内容(在我重新格式化之前)。

first="1",       rest=["1 1", "1 1 1", "1 1 1 2", "1 2 3 1", "1 2", "2 3"]
first="1 1",     rest=["1 1 1", "1 1 1 2", "1 2 3 1", "1 2", "2 3"]
first="1 1 1",   rest=["1 1 1 2", "1 2 3 1", "1 2", "2 3"]
first="1 1 1 2", rest=["1 2 3 1", "1 2", "2 3"]
first="1 2 3 1", rest=["1 2", "2 3", "1 1 1 2"]
first="1 2",     rest=["2 3", "1 1 1 2", "1 2 3 1"]
first="2 3",     rest=["1 1 1 2", "1 2 3 1"]

【讨论】:

我从未听说过rotate,+1! Cary,我在算法中做了一个简单的更改,使用“delete_if”而不是拒绝。这具有删除子字符串的预期副作用,因为它们像您的算法一样被发现。在 ruby​​ 中解决问题的所有不同方法总是让我感到惊讶。您对 shift 和 rotate 的使用很好地提醒您探索 ruby​​ 中可用的所有方法。 很高兴您提到了您所做的更改。我之前看到了你的答案,然后在发布后我又看到了你的答案,它似乎与我记忆中的不同,但我没有注意到你已经编辑了。很高兴知道我的短期记忆还不错。【参考方案3】:

它使用更少的内存,执行更少的计算。 这会以两种方式删除子字符串,循环会更少。 带来了

             user       system     total       real
    First    0.000000   0.000000   0.000000 (  0.000076)
    Second   0.010000   0.000000   0.010000 (  0.000037)
    Third    0.000000   0.000000   0.000000 (  0.000019)

上面提到的是上面提到的 2 个算法(第一个和第二个)和这个(第三个)的基准测试结果。

array = ['1 1 1', '1', '1 1', '1 1 1 2', '1 2 3 1', '1 2', '2 3', '1 2 3', '1 1 1']

i1 = 0
arr_len = array.length
last_index = arr_len - 1

while i1 <= last_index
  w1 = array[i1]
  i2 = i1 + 1
  while i2 <= last_index
    w2 = array[i2]
    # If w2 is a subset of w1
    if w1.include? w2
      # Delete from index i2
      array.delete_at(i2)
      # Decrement the array_length as one element is deleted
      arr_len -= 1
      # Decrement last index, as one element is deleted
      last_index -= 1
      next
    end
    # If w1 comes out to be a subset of w2
    if w2.include? w1
      # Delete the value from that index
      array.delete_at(i1)
      # Decrement the array_length as one element is deleted
      arr_len -= 1
      # Decrement last index, as one element is deleted
      last_index -= 1
      # Reset value of w1 as it is deleted in this operation
      w1 = array[i1]
      # Reset index of 2nd loop to start matching again
      i2 = i1 + 1
      # Move next from here only
      next
    end
    i2 += 1
  end
  i1 += 1
end

【讨论】:

以上是关于有效删除Ruby中数组中其他元素的所有子字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何更改python字符串子字符串信息

PB中取字符串子串的函数是啥

数组篇在python中如何查找最长字符串子串

从Ruby中的数组中删除重复元素

PB中取字符串子串的函数是啥

每日一题-Day10-无重复最长字串