【问题标题】:Fast anagram solving快速解谜
【发布时间】:2013-02-28 17:13:49
【问题描述】:

给定两个字符串,我想确定它们是否是彼此的字谜。这是我想出的解决方案:

# output messages
def anagram
    puts "Anagram!"
    exit 
end

def not_anagram
    puts "Not an anagram!"
    exit
end

# main method
if __FILE__ == $0
   # read two strings from the command line
   first, second = gets.chomp, gets.chomp

   # special case 1
   not_anagram if first.length != second.length

   # special case 2
   anagram if first == second

   # general case
   # Two strings must have the exact same number of characters in the
   # correct case to be anagrams.
   # We can sort both strings and compare the results
   if first.chars.sort.join == second.chars.sort.join
      anagram
   else
      not_anagram
   end
end

但我认为可能有更好的。我分析了这个方案的效率,得出了:

  • chars:将字符串拆分为字符数组O(n)
  • sort:按字母顺序对字符串进行排序,我不知道在 Ruby 中排序是如何实现的,但我假设 O(n log n),因为这是众所周知的排序效率
  • join:从字符数组构建一个字符串O(n)
  • ==:字符串比较本身必须检查字符串2*O(n) 的每个字符

鉴于上述情况,我将整个解决方案的效率归类为O(n log n),因为排序效率最高。有没有比O(n log n) 更有效的方法来做到这一点?

【问题讨论】:

    标签: ruby algorithm big-o anagram


    【解决方案1】:

    你的大 O 应该是O(n*lg(n)),因为排序是限制函数。如果您尝试使用非常大的字谜,您会发现O(n) 解决方案的性能损失高于预期。

    您可以通过比较两个字符映射中的计数 => 字符计数来执行O(n) 解决方案。

    肯定有其他解决方案的复杂性大致相同,但我认为您无法比O(n) 更快地提出任何解决方案

    【讨论】:

    • 对不起,我的意思是把 n log n 写在我的笔记里,我只是将错误的公式复制到问题中。
    • +1。严格来说,计数解是 O(max(n,|alphabet|))。字母表的大小在技术上是恒定的,但如果字母表是 unicode 并且字符串不庞大,它将占主导地位。
    • 计数解决方案是,遍历两个字符串散列每个字符并在进行时更新其计数?我有点想这个问题总是O(n),因为比较总是以整个字符串长度为界。
    • 是的。将每个角色的计数存储在不同的地图中,然后在最后比较地图。
    • @DanielWilliams 谢谢,这真的很有帮助。我还阅读了一个解决方案,它计算字符串中每个字符的字节值,但由于可能发生冲突而将其丢弃。
    【解决方案2】:

    计数示例:

    def anagram?(str_a, str_b)
      if str_a.length != str_b.length
        false
      else
        counts = Hash.new(0)
        str_a.each_char{ |c| counts[c] += 1 }
        str_b.chars.none?{ |c| (counts[c] -= 1) < 0 }
      end
    end
    
    anagram? 'care', 'race'
    # => true
    anagram? 'cat', 'dog'
    # => false
    

    【讨论】:

    • anagram? 'cats', 'cat' 将返回 true。
    • @JuanLopes 'cats', 'cat' 不会到达else 块,因为它们的长度不同。所以它实际上会返回false
    【解决方案3】:

    您可以在O(n+m) 中执行此操作,其中 m 是字母的长度

    1.创建一个大小等于输入字母大小的数组。

    2.将数组中的所有值初始化为'0'。

    3.扫描第一个输入字符串,为每个字符递增数组中的相应值(如在找到字母表中的第一个字母时递增数组[0])。

    4. 对第二个字符串重复相同的操作,但在这种情况下,数组中的值需要递减。

    如果数组中的所有值都是'0',那么这两个字符串是字谜,否则它们不是。

    【讨论】:

    • @funtime n 是两个字符串的长度,所以这个算法实际上是O(m) 其中m 是使用的字母表的长度,但仍然是一个很好的解决方案。
    【解决方案4】:

    我需要一些东西来检查字谜,然后想出了这个:

    def string_to_array(s)
      s.downcase.gsub(/[^a-z]+/, '').split('').sort
    end
    
    def is_anagram?(s1, s2)
      string_to_array(s1) == string_to_array(s2)
    end
    
    puts is_anagram?("Arrigo Boito",       "Tobia Gorrio")
    puts is_anagram?("Edward Gorey",       "Ogdred Weary")
    puts is_anagram?("Ogdred Weary",       "Regera Dowdy")
    puts is_anagram?("Regera Dowdy",       "E. G. Deadworry")
    puts is_anagram?("Vladimir Nabokov",   "Vivian Darkbloom")
    puts is_anagram?("Vivian Darkbloom",   "Vivian Bloodmark")
    puts is_anagram?("Dave Barry",         "Ray Adverb")
    puts is_anagram?("Glen Duncan",        "Declan Gunn")
    puts is_anagram?("Damon Albarn",       "Dan Abnormal")
    puts is_anagram?("Tom Cruise",         "So I'm cuter")
    puts is_anagram?("Tom Marvolo Riddle", "I am Lord Voldemort")
    puts is_anagram?("Torchwood",          "Doctor Who")
    puts is_anagram?("Hamlet",             "Amleth")
    puts is_anagram?("Rocket boys",        "October Sky")
    puts is_anagram?("Imogen Heap",        "iMegaphone")
    

    【讨论】:

    • 我会使用/[^[:alpha:]+]/ 这样其他语言就不会破坏它
    • 感谢您的解决方案,不幸的是,对于这个问题,我的字谜区分大小写并且允许使用空格。所以"the eyes" 不是"theeyes" 的字谜。
    猜你喜欢
    • 2021-04-21
    • 1970-01-01
    • 1970-01-01
    • 2012-05-08
    • 2015-10-31
    • 2015-07-03
    • 2014-10-14
    • 2018-07-08
    • 2011-10-14
    相关资源
    最近更新 更多