Ruby 字谜拼图

Ruby Anagrams puzzle

提问人:Precious 提问时间:12/14/2022 最后编辑:Holger JustPrecious 更新时间:12/16/2022 访问量:105

问:

我是 Ruby 中的乞丐,我需要有关如何让程序返回包含“入口”的列表的说明

问题:给定一个单词和一个可能的字谜列表,选择正确的子列表。

给定“listen”和候选列表,如“enlists”、“google”、“inlets”、“banana”,程序应该返回一个包含“inlets”的列表。

这就是我能够做到的

puts 'Enter word'
word_input = gets.chomp
puts 'Enter anagram_list'
potential_anagrams = gets.chomp
potential_anagrams.each do |anagram|
end

假设我的word_input是,这就是程序应该如何运行,但我不知道如何让它工作。"hello"

is_anagram? word: 'hello', anagrams: ['helo', 'elloh', 'heelo', 'llohe']
# => 'correct anagrams are: elloh, llohe'

真的很感激想法。

Ruby 方法 Anagram

评论

1赞 Konstantin Strukov 12/14/2022
您的起点是正确的任务定义。你需要找到字谜,但字谜是什么?“根据计算”定义它,您将获得 80% 的解决方案。为什么“elloh”是“hello”的字谜,而“eloh”或“ellloh”不是?它们有何不同?
0赞 Precious 12/14/2022
hello“ - elloh,因为 hello 中的字符在 elloh 中是相同的。它只是被重新排列了。
1赞 user1934428 12/14/2022
将每个字符串转换为字符。然后比较设置相等性。请参阅此处如何在 Ruby 中使用 a。SetSet
1赞 tadman 12/14/2022
提示:s.chars.sort.join('')
3赞 Chris 12/14/2022
@user1934428:请记住"helllllo".chars.to_set == "hello".chars.to_s => true

答:

1赞 Chris 12/14/2022 #1

正如注释中所暗示的那样,字符串可以很容易地转换为其字符数组。

irb(main):005:0> "hello".chars
=> ["h", "e", "l", "l", "o"]
irb(main):006:0> "lleho".chars
=> ["l", "l", "e", "h", "o"]

数组可以很容易地排序。

irb(main):007:0> ["h", "e", "l", "l", "o"].sort
=> ["e", "h", "l", "l", "o"]
irb(main):008:0> ["l", "l", "e", "h", "o"].sort
=> ["e", "h", "l", "l", "o"]

并且可以比较数组。

irb(main):009:0> ["e", "h", "l", "l", "o"] == ["e", "h", "l",
l", "o"]
=> true

把所有这些放在一起,你应该能够确定一个词是否是另一个词的字谜。然后,您可以将其与数组中的字谜配对以查找字谜。像这样:#select

def is_anagram?(word, words)
  words.select do |w|
    ...
  end
end

评论

0赞 Precious 12/14/2022
如果您需要将一个字符串与包含多个字符串的数组进行比较,该怎么办?
0赞 Chris 12/15/2022
这就是最后一点代码的用途。 将返回该表达式所属的任何字符串的数组。...#selectwordstrue
1赞 Cary Swoveland 12/15/2022 #2

这是我的做法。

方法

def anagrams(list, word)
  ltr_freq = word.each_char.tally
  list.select { |w| w.size == word.size && w.each_char.tally == ltr_freq }
end

list = ['helo', 'elloh', 'heelo', 'llohe']
anagrams(list, 'hello')
  #=> ["elloh", "llohe"]

计算复杂度

出于实际目的,任何长度的字的计算复杂度都可以看作是 O()。这是因为哈希键查找几乎是恒定时间的。因此,确定一个长度的单词是否是另一个相同长度的单词的字谜的计算复杂度可以看作是 O()。w.each_char.tallywnnnn

这与对单词字母进行排序的方法相比,后者的计算复杂度为 O(n*log(n)),n 是单词长度。

解释

请参阅 Enumerable#tally。请注意,当 是 时不计算。w.each_char.tallyw.size == word.sizefalse

现在让我们添加一些语句来查看发生了什么。puts

def anagrams(list, word)
  ltr_freq = word.each_char.tally
  puts "ltr_freq = #{ltr_freq}"
  list.select do |w|
    puts "\nw = '#{w}'"
    if w.size != word.size
      puts "words differ in length"
      false
    else
      puts "w.each_char.tally = #{w.each_char.tally}"
      if w.each_char.tally == ltr_freq
        puts "character frequencies are the same"
        true
      else
        puts "character frequencies differ"
        false
      end
    end
  end
end
anagrams(list, 'hello')
ltr_freq = {"h"=>1, "e"=>1, "l"=>2, "o"=>1}

w = 'helo'
words differ in length

w = 'elloh'
w.each_char.tally = {"e"=>1, "l"=>2, "o"=>1, "h"=>1}
character frequencies are the same

w = 'heelo'
w.each_char.tally = {"h"=>1, "e"=>2, "l"=>1, "o"=>1}
character frequencies differ

w = 'llohe'
w.each_char.tally = {"l"=>2, "o"=>1, "h"=>1, "e"=>1}
character frequencies are the same
  #=>["elloh", "llohe"]

可能的改进

表达式的潜在弱点

w.each_char.tally == ltr_freq

就是在得出结论之前必须确定单词中所有唯一字母的频率,即使例如,单词中的第一个字母没有出现在单词中。我们可以采取以下补救措施。wwword

def anagrams(list, word)
  ltr_freq = word.each_char.tally
  list.select do |w|
    next false unless w.size == word.size
    ltr_freqs_match?(w, ltr_freq.dup)
  end
end
def ltr_freqs_match?(w, ltr_freq)
  w.each_char do |c|
    return false unless ltr_freq.key?(c)
    ltr_freq[c] -= 1
    ltr_freq.delete(c) if ltr_freq[c].zero?
  end
  true
end

人们必须根据上述原始版本测试此变体,以确定哪个版本最快。这种变体的优点是,一旦发现单词中给定字符的累积计数大于相同字母的总数,它就会终止(短路)比较。同时,它是用 C 语言编写的,所以它可能仍然更快。anagramswwordtally

1赞 pjs 12/15/2022 #3

我采取了创建一个类来追求可重用性的策略。对于一次性用法来说,这是矫枉过正的,但允许您建立一组已知单词,然后根据需要多次轮询它以获取多个候选单词的字谜。

此解决方案基于哈希和集合构建,使用单词的排序字符作为共享相同字母的一组单词的索引。散列是 O(1),集合是 O(1),如果我们将单词视为具有有界长度,则键的计算也是 O(1),从而产生每个单词的恒定时间的整体复杂度。

我已经注释了代码,但如果有任何不清楚的地方,请随时询问。

require 'set'

class AnagramChecker
  def initialize
    # A hash whose default value is an empty set object
    @known_words = Hash.new { |h, k| h[k] = Set.new }
  end

  # Create a key value from a string by breaking it into individual
  # characters, sorting, and rejoining, so all strings which are
  # anagrams of each other will have the same key.
  def key(word)
    word.chars.sort.join
  end

  # Add individual words to the class by generating their key value
  # and adding the word to the set. Using a set guarantees no
  # duplicates of the words, since set contents are unique.
  def add_word(word)
    word_key = key(word)
    @known_words[word_key] << word
    # return the word's key to avoid duplicate work in find_anagrams
    word_key
  end

  def find_anagrams(word)
    word_key = add_word(word)     # add the target word to the known_words
    @known_words[word_key].to_a   # return all anagramatic words as an array
  end

  def inspect
    p @known_words
  end
end

生成已知单词库如下所示:

ac = AnagramChecker.new
["enlists", "google", "inlets", "banana"].each { |word| ac.add_word word }

ac.inspect   # {"eilnsst"=>#<Set: {"enlists"}>, "eggloo"=>#<Set: {"google"}>, "eilnst"=>#<Set: {"inlets"}>, "aaabnn"=>#<Set: {"banana"}>}

使用它看起来像:

p ac.find_anagrams("listen")  # ["inlets", "listen"]
p ac.find_anagrams("google")  # ["google"]

如果您不希望目标词包含在输出中,请进行相应的调整。find_anagrams