这里有两种方法可以做到。
比较计数哈希
def uses_available_letters?(word, letters_in_hand)
word_tally = word.each_char.tally
letters_in_hand_tally = letters_in_hand.tally
word_tally.all? { |k,v| letters_in_hand_tally[k].to_i >= v }
end
letters_in_hand = ["b", "l", "c", "e", "o", "h", "e", "a"]
uses_available_letters?("belch", letters_in_hand)
#=> true
uses_available_letters?("belche", letters_in_hand)
#=> true
uses_available_letters?("beleche", letters_in_hand)
#=> false
当word = "beleche",
word_tally
#=> {"b"=>1, "e"=>3, "l"=>1, "c"=>1, "h"=>1}
letters_in_hand_tally
#=> {"b"=>1, "l"=>1, "c"=>1, "e"=>2, "o"=>1, "h"=>1, "a"=>1}
请参阅Enumerable#tally,它是在 Ruby v2.7 中引入的。为了支持早期版本的 Ruby,可以使用 Hash::new 的形式,它接受一个参数(这里为零),称为 默认值,并且没有块。
def uses_available_letters?(word, letters_in_hand)
word_tally = tally_ho(word.chars)
letters_in_hand_tally = tally_ho(letters_in_hand)
word_tally.all? { |k,v| letters_in_hand_tally[k].to_i >= v }
end
def tally_ho(arr)
arr.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
end
如果letters_in_hand_tally没有键k,letters_in_hand_tally[k] #=> nil,导致比较nil >= v,会引发异常。正是出于这个原因,我写了letters_in_hand_tally[k].to_i,因为nil.to_i #=> 0 并且word_tally 的所有值都是正整数。
也可以将块写成如下:
{ |k,v| letters_in_hand_tally.key?(k) && letters_in_hand_tally[k] >= v }
将letters_in_hand 中的重复字母视为不同的字母
第二种方法假定letters_in_hand 由字符串而不是数组给出。例如:
letters_in_hand = "blceohea"
当然,作为第一步,我们总是可以从数组中形成该字符串,但我认为letters_in_hand 是一个字符串更令人愉悦,就像word 一样。以下方法利用String#index 接受可选的第二个参数这一事实,该参数等于开始搜索的字符串的索引:
def uses_available_letters?(word, letters_in_hand)
start_index = Hash.new(0)
word.each_char.all? do |c|
i = letters_in_hand.index(c, start_index[c])
return false if i.nil?
start_index[c] = i + 1
end
true
end
uses_available_letters?("belch", letters_in_hand)
#=> true
uses_available_letters?("belche", letters_in_hand)
#=> true
uses_available_letters?("belecher", letters_in_hand)
#=> false