我正在浏览我的系统字典,并根据严格的定义查找任何其他单词的子集和超集的单词。
下面的实现不起作用,但如果确实如此,我认为它会非常有效。如何迭代数组并在迭代期间从同一个数组中删除项目?
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_words
和find_subset_words
都返回哈希值,因此values.flatten
位
答案 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
的每个索引s1
,s1[i] = s2[m(i)]
;和i < j
则m(i) < m(j)
。当且仅当s2
是s1
的子集时,s1
才是s2
的超集。
请注意,要s1
成为s2
的子集,s1.size <= s2.size
必须为true。
例如:
m(0) = 3
和m(1) = 2
,但m(0) < m(1)
为false; <强>算法强>
子集(因而是超集)是一种传递关系,它允许显着的算法效率。我的意思是,如果s1
是s2
的子集而s2
是s3
的子集,则s1
是s3
的子集}。
我将按如下方式进行:
neither_sub_nor_sup
和longest_sups
以及空数组subs_and_sups
。w
添加到neither_sub_nor_sup
,其中w
是字典中最长的字词。w
(最长到最短),执行以下操作:
u
的每个元素neither_sub_nor_sup
确定w
是u
的子集。如果是,请将u
从neither_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
不再是与该附加要求的传递关系。我没有调查其含义,但我怀疑这意味着需要一个相当野蛮的算法,可能需要对字典中所有单词进行成对比较。