通过枚举动态删除数组中的元素

时间:2015-07-13 03:40:55

标签: arrays ruby algorithm

我正在浏览我的系统字典,并根据严格的定义查找任何其他单词的子集和超集的单词。

下面的实现不起作用,但如果确实如此,我认为它会非常有效。如何迭代数组并在迭代期间从同一个数组中删除项目?

def collect_dead_words
  result = @file #the words in my system dictionary, as an array
  wg = WordGame.new # the class that "knows" the find_subset_words & 
                    # find_superset_words methods
  result.each do |value|
    wg.word = value
    supersets = wg.find_superset_words.values.flatten
    subsets = wg.find_subset_words.values.flatten
    result.delete(value) unless (matches.empty? && subsets.empty?)
    result.reject! { |cand| supersets.include? cand }
    result.reject! { |cand| subsets.include? cand }
  end
  result
end

注意:find_superset_wordsfind_subset_words都返回哈希值,因此values.flatten

4 个答案:

答案 0 :(得分:5)

在迭代时修改集合是不可取的。相反,要么迭代集合的副本,要么创建一个单独的数组,以便稍后删除。

答案 1 :(得分:1)

实现此目的的一种方法是使用Array#delete_if。这是我的运行,所以你明白了:

supersets_and_subsets = []

result.delete_if do |el|
  wg.word = el
  superset_and_subset = wg.find_superset_words.values.flatten + wg.find_subset_words.values.flatten
  supersets_and_subsets << superset_and_subset

  !superset_and_subset.empty?
end

result -= supersets_and_subsets.flatten.uniq

答案 2 :(得分:1)

以下是我根据您的反馈提出的建议(加上从最短的单词开始的进一步优化):

def collect_dead_words
  result = []
  collection = @file
  num = @file.max_by(&:length).length
  1.upto(num) do |index|
    subset_by_length = collection.select {|word| word.length == index }
    while !subset_by_length.empty? do
      wg = WordGame.new(subset_by_length[0])
      supermatches = wg.find_superset_words.values.flatten
      submatches = wg.find_subset_words.values.flatten
      collection.reject! { |cand| supermatches.include? cand }
      collection.reject! { |cand| submatches.include? cand }
      result << wg.word if (supermatches.empty? && submatches.empty?)
      subset.delete(subset_by_length[0])
      collection.delete(subset_by_length[0])
    end
  end
  result
end

欢迎进一步优化!

答案 3 :(得分:1)

问题

据我了解,如果从s1删除零个或多个字符后s2,则字符s1 == s2是字符串s2的子集;也就是说,如果存在m索引的映射s1,那么 1

  • 代表i的每个索引s1s1[i] = s2[m(i)];和
  • 如果i < jm(i) < m(j)

当且仅当s2s1的子集时,s1才是s2的超集。

请注意,要s1成为s2的子集,s1.size <= s2.size必须为true。

例如:

  • “cat”是“craft”的子集,因为如果删除“r”和“f”,后者就变成“cat”。
  • “cat”不是“cutie”的子集,因为“cutie”没有“a”。
  • “cat”不是“at”的超集因为“cat”.include?(“at”)#=&gt; TRUE`。
  • “cat”不是“enact”的子集,因为m(0) = 3m(1) = 2,但m(0) < m(1)为false;

<强>算法

子集(因而是超集)是一种传递关系,它允许显着的算法效率。我的意思是,如果s1s2的子集而s2s3的子集,则s1s3的子集}。

我将按如下方式进行:

  • 创建空集neither_sub_nor_suplongest_sups以及空数组subs_and_sups
  • 按长度排序字典中的单词,最长的。
  • w添加到neither_sub_nor_sup,其中w是字典中最长的字词。
  • 对于词典中的每个后续单词w(最长到最短),执行以下操作:
    • u的每个元素neither_sub_nor_sup确定wu的子集。如果是,请将uneither_sub_nor_sup移至longest_sups,然后将u追加至subs_and_sups
    • 如果一个或多个元素已从neither_sub_nor_sup移至longest_sups,请将w附加到subs_and_sups;否则将w添加到neither_sub_nor_sup
  • 返回subs_and_sups

<强>代码

require 'set'

def identify_subs_and_sups(dict)
  neither_sub_nor_sup, longest_sups = Set.new, Set.new
  dict.sort_by(&:size).reverse.each_with_object([]) do |w,subs_and_sups|
    switchers = neither_sub_nor_sup.each_with_object([]) { |u,arr|
      arr << u if w.subset(u) }
    if switchers.any?
      subs_and_sups << w
      switchers.each do |u|
        neither_sub_nor_sup.delete(u)
        longest_sups << u
        subs_and_sups << u
      end
    else
      neither_sub_nor_sup << w
    end
  end
end

class String
  def subset(w)
    w =~ Regexp.new(self.gsub(/./) { |m| "#{m}\\w*" })
  end
end

示例

dict = %w| cat catch craft cutie enact trivial rivert river |  
  #=> ["cat", "catch", "craft", "cutie", "enact", "trivial", "rivert", "river"] 
identify_subs_and_sups(dict)
  #=> ["river", "rivert", "cat", "catch", "craft"]

<强>变体

我们不是将字典中的单词从最长到最短处理,而是将它们排序为最短到最长:

def identify_subs_and_sups1(dict)
  neither_sub_nor_sup, shortest_sups = Set.new, Set.new
  dict.sort_by(&:size).each_with_object([]) do |w,subs_and_sups|
    switchers = neither_sub_nor_sup.each_with_object([]) { |u,arr|
      arr << u if u.subset(w) }
    if switchers.any?
      subs_and_sups << w
      switchers.each do |u|
        neither_sub_nor_sup.delete(u)
        shortest_sups << u
        subs_and_sups << u
      end
    else
      neither_sub_nor_sup << w
    end
  end
end

identify_subs_and_sups1(dict)
  #=> ["craft", "cat", "rivert", "river"]

<强>基准

(继续......)

1 OP声明(在后面的评论中){​​{1}}如果s1不是s2的子字符串。我打算假装我从未见过它,因为它会把扳手扔进工作中。不幸的是,s2.include?(s1) #=> true不再是与该附加要求的传递关系。我没有调查其含义,但我怀疑这意味着需要一个相当野蛮的算法,可能需要对字典中所有单词进行成对比较。