【问题标题】:How to elegantly compute the anagram signature of a word in ruby?如何优雅地计算 ruby​​ 中单词的字谜签名?
【发布时间】:2012-05-01 18:06:56
【问题描述】:

由于this 问题,我正在寻找一种优雅的(红宝石)方法来计算this 答案中建议的单词签名。

建议的想法是对单词中的字母进行排序,并对重复的字母进行运行长度编码。因此,例如“mississippi”首先变为“iiiimppssss”,然后可以通过编码为“4impp4s”进一步缩短。

我对 ruby​​ 比较陌生,虽然我可以一起破解一些东西,但我相信这对于有更多 ruby​​ 经验的人来说是一个单一的衬里。我很想看看人们的方法并提高我的红宝石知识。

编辑:澄清一下,计算签名的性能对我的应用程序并不重要。我正在寻找计算签名,以便我可以将它与每个单词一起存储在一个大型单词数据库(450K 单词)中,然后查询具有相同签名的单词(即给定单词的所有字谜,它们是实际的英文单词)。因此专注于空间。 “优雅”的部分只是为了满足我的好奇心。

【问题讨论】:

    标签: ruby algorithm


    【解决方案1】:

    创建字母排序列表的最快方法是:

    "mississippi".unpack("c*").sort.pack("c*")
    

    它比 split('') 和 join() 快很多。为了比较,最好将数组重新打包成一个字符串,这样您就不必比较数组了。

    【讨论】:

    • 你能用测量来支持这个说法吗?例如(/usr/dict/words?)在 10,000 个单词的列表上要快多少?
    • 这很有趣,但很神秘。
    • 可以快速比较一下:5.5 秒与 Ruby 1.8.7 的 8.5 秒,因此解压和打包要快一些。我认为 JRuby 的差异更大,但我真的不记得了。我已经为我的字谜查找器完成了这个调整:martin.ankerl.com/2008/08/09/two-word-anagram-finder-algorithm
    • 5.5 秒是在 320,000 字的字典上
    【解决方案2】:

    我也不太喜欢 Ruby,但正如我在其他评论中指出的那样,这似乎适用于所描述的算法。

    s = "mississippi"
    s.split('').sort.join.gsub(/(.)\1{2,}/) { |s| s.length.to_s + s[0,1] } 
    

    当然,您需要确保单词是小写的,不包含数字等。

    根据要求,我将尝试解释代码。如果我没有正确理解所有 Ruby 或 reg ex 术语,请原谅我,但这里有。

    我认为拆分/排序/连接部分非常简单。对我来说有趣的部分始于对 gsub 的调用。这会将匹配正则表达式的子字符串替换为其后面块的返回值。 reg ex 找到任何字符并创建反向引用。那是“(。)”部分。然后,我们使用反向引用“\1”继续匹配过程,该反向引用评估匹配的第一部分找到的任何字符。我们希望该字符至少再出现两次,至少出现 3 次。这是使用量词“{2,}”完成的。

    如果找到匹配项,则匹配的子字符串将作为参数传递给下一个代码块,这要归功于“|s|”部分。最后,我们使用匹配子字符串长度的字符串等价物,并将组成该子字符串的任何字符附加到它(它们应该都是相同的)并返回连接的值。返回的值替换原始匹配的子字符串。整个过程一直持续到没有任何东西可以匹配,因为它是对原始字符串的全局替换。

    如果这令人困惑,我深表歉意。通常情况下,我更容易将解决方案形象化而不是解释清楚。

    【讨论】:

    • +1 这是我想出的确切解决方案,经过一段时间的思考,我没有看到更简单的方法。
    • 我编辑了答案以适应没有滚动条的页面,并将 // 更改为 '' 以更好地使用语法突出显示(它的东西 // 开始评论)。
    • 似乎工作得很好......你能编辑解释一下它是如何工作的吗?
    • split/sort/join 将字符串转换为字符数组,对它们进行排序,并将排序后的结果转换回字符串。 gsub 用运行的长度和运行中的第一个字符替换任何运行 3 个或更多相同字符的运行(在运行中都将相同)。
    【解决方案3】:

    我没有看到一个优雅的解决方案。您可以使用split 消息将字符放入一个数组中,但是一旦您对列表进行了排序,我就看不到一个很好的线性时间连接原语来返回一个字符串。我很惊讶。

    顺便说一下,游程编码几乎可以肯定是浪费时间。在我认为值得考虑之前,我必须看到一些非常令人印象深刻的测量结果。如果您避免运行长度编码,您可以对任何字符串进行语法化,而不仅仅是一串字母。如果你知道你只有字母并且想节省空间,你可以将它们 5 位打包成一个字母。

    ---伊尔玛·维普


    编辑:另一张海报找到了我错过的join。不错。

    【讨论】:

    • 您可以使用例如 str.scan(/./) 或 str.split('') 来形成一个字符数组,对数组进行排序,然后将它与 String 连接在一起#join,虽然没有必要将其重新加入字符串。
    • +1 指出 RLE 可能不会节省太多空间
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-26
    • 1970-01-01
    • 2019-11-15
    • 2022-11-18
    • 2011-05-07
    • 2021-10-16
    • 2011-01-29
    相关资源
    最近更新 更多