【问题标题】:Checking that string contains only unique array elements, repeating letters检查字符串仅包含唯一的数组元素,重复字母
【发布时间】:2021-01-04 21:51:06
【问题描述】:

我正在创建一个方法来测试一个字符串是否只包含拼字游戏中的数组元素。 给定这个数组:

hand = ["b", "l", "c", "o", "h", "e", "a"] 如果单词是 "beach"

原来我用的是这个块:

def uses_available_letters?(word, letters_in_hand)
  input = word.split("")
  input.index{ |x| !letters_in_hand.include?(x) }.nil?
end

给定这个数组:

hand = ["b", "l", "c", "o", "h", "e", "a", "c"] 

如果 word = "beach",方法返回 true。但是,如果 word = "beeeach",即使数组只包含 1 个 "e",它仍然会返回 true。

所以,我尝试在比较后删除数组元素:

def uses_available_letters?(word, letters_in_hand)
  input = word.split("") 
  input.each do 
    if letters_in_hand.include?(input[i])
      letters_in_hand.delete(input[i])
    else
      return false 
    end 
  end
end 

但是,给定“beacch”,即使数组中有 2 个 c,也会返回 false。所以似乎每一个喜欢的字母都被删除了。

发送帮助!

【问题讨论】:

  • 提示:word.chars 是一种获取信件的简单方法。

标签: arrays ruby methods include


【解决方案1】:

Ruby 数组操作的问题在于将它们视为一组唯一值,如下所示:

%w[ a a b b c c ] - %w[ a b c ]
# => []

这会删除每个abc,而不仅仅是第一个。 delete 也是如此,除非您使用非常具体的索引,但由于删除会随机播放剩余的索引等,这会很快变得混乱。

我会考虑将手存储为字母/计数对,如下所示:

def hand_count(str)
  str.chars.group_by(&:itself).map { |l,a| [ l, a.length ] }.to_h
end

这会给你一个哈希而不是一个数组:

hand_count('example')
# => {"e"=>2, "x"=>1, "a"=>1, "m"=>1, "p"=>1, "l"=>1}

所以现在你可以写一个“sub”方法了:

def hand_sub(str, sub)
  hand = hand_count(str)

  hand_count(sub).each do |l, c|
    # Unless the letter is present in the hand...
    unless (hand.key?(l))
      # ...this isn't possible.
      return false
    end

    # Subtract letter count
    hand[l] -= c

    # If this resulted in a negative number of remaining letters...
    if (hand[l] < 0)
      # ...this isn't possible.
      return false
    end
  end

  # Convert back into a string...
  hand.map do |l, c|
    # ...by repeating each letter count times.
    l * c
  end.join
end

这很简单:

hand_sub('example', 'exam')
# => "epl"
hand_sub('example', 'expel')
# => "am"
hand_sub('example', 'plexi')
# => false
hand_sub('example', 'ell')
# => false

【讨论】:

  • 你也可以to_h { |l,a| [ l, a.length ] } (Ruby 2.6+)。
【解决方案2】:
def uses_available_letters?(word, hand)
  result = true
  word.split('').each do |letter|
    if hand.include?(letter)
      hand.delete_at(hand.index(letter))
    else
      result = false
    end
  end
  result
end

2.6.5 :065 > uses_available_letters?('beeeach', %w[b l e h c a e e])
 => true 
2.6.5 :066 > uses_available_letters?('beach', %w[b l e h c a])
 => true 
2.6.5 :067 > uses_available_letters?('beeeach', %w[b l e h c a])
 => false 
2.6.5 :068 > uses_available_letters?('beeeach', %w[b l e d h e c e r a])
 => true 

【讨论】:

  • 如果hand = hand = %w[b l c o h e a e e]uses_available_letters?('beeeach', hand) #=&gt; false,但应该返回true
  • 谢谢!我正在编辑添加索引和排序的代码
【解决方案3】:

这里有两种方法可以做到。

比较计数哈希

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没有键kletters_in_hand_tally[k] #=&gt; nil,导致比较nil &gt;= v,会引发异常。正是出于这个原因,我写了letters_in_hand_tally[k].to_i,因为nil.to_i #=&gt; 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-01
    • 1970-01-01
    • 2021-05-02
    • 2014-10-01
    • 1970-01-01
    • 2014-07-16
    相关资源
    最近更新 更多