【问题标题】:Generate every possible capitalization of a String生成字符串的所有可能大小写
【发布时间】:2021-08-31 12:07:28
【问题描述】:

给定一个任意的输入字符串,我怎样才能生成一个包含每个可能的大写字符的序列?

例如给定输入 "abc",预期输出将是这 8 个字符串的序列:

  • ABC
  • ABc
  • AbC
  • Abc
  • aBC
  • aBc
  • abC
  • abc

【问题讨论】:

    标签: ruby string


    【解决方案1】:
    # Returns an enumerator that yields every possible capitalization of the input string
    # @param [String] input
    # @return [Enumerator::Lazy]
    def all_possible_capitalizations(input)
      chars = input.downcase.chars
    
      [true, false]
        .repeated_permutation(chars.count)
        .lazy
        .map do |permutation|
          permutation
            .zip(chars)
            .map { |should_capitalize, char| should_capitalize ? char.upcase : char }
            .join
        end
    end
    
    all_possible_capitalizations("abc").each do |s|
      puts s
    end
    

    您可以在[false, true] 上使用Array#repeated_permutation 创建一个枚举器,该枚举器产生具有给定长度的每个可能的位字符串,例如:

    1. [true, true, true]
    2. [true, true, false]
    3. [true, false, true]
    4. [true, false, false]
    5. [false, true, true]
    6. [false, true, false]
    7. [false, false, true]
    8. [false, false, false]

    您可以使用输入的字符压缩其中的每一个:

    1. [[true, "a"], [true, "b"], [true, "c"]]
    2. [[true, "a"], [true, "b"], [false, "c"]]
    3. [[true, "a"], [false, "b"], [true, "c"]]
    4. [[true, "a"], [false, "b"], [false, "c"]]
    5. [[false, "a"], [true, "b"], [true, "c"]]
    6. [[false, "a"], [true, "b"], [false, "c"]]
    7. [[false, "a"], [false, "b"], [true, "c"]]
    8. [[false, "a"], [false, "b"], [false, "c"]]

    然后,您可以映射这些对中的每一个,并使用排列中的布尔值来决定天气以大写成对的字符,从而为您提供:

    1. ["A", "B", "C"]
    2. ["A", "B", "c"]
    3. ["A", "b", "C"]
    4. ["A", "b", "c"]
    5. ["a", "B", "C"]
    6. ["a", "B", "c"]
    7. ["a", "b", "C"]
    8. ["a", "b", "c"]

    然后你只需join每个结果,将字符数组转换为字符串。

    【讨论】:

    • 您也可以在 [:upcase, :downcase ] 上使用 Array#repeated_permutation(input.size),用字符和char.send(a_case) 等压缩排列。
    • 哦,这也是一个有趣的方法,我猜它替换了中间的条件语句map
    • 懒惰似乎是多余的还是我错过了什么?在没有它的情况下运行您的示例,它似乎可以工作
    • @JoelBlum 这就是重点,lazy 不会更改结果值,它只是使它们不会全部同时保存在内存中(这对于像这样的几个字符长的字符串,但对于较长的字符串会是一个问题)。
    【解决方案2】:

    只是为了好玩,这里有一个 1-liner 解决方案。可能会以某种方式清理,但这只是我很快想到的:

    def all_possible_capitalisations(string)
      string
        .chars
        .map { |char| [char, char.swapcase] }
        .inject { |acc, chars| chars.flat_map { |char| acc.zip([char] * acc.count)} }
        .map(&:join)
    end
    
    all_possible_capitalisations("abc")
      # => ["abc", "Abc", "aBc", "ABc", "abC", "AbC", "aBC", "ABC"]
    

    它是如何工作的?让我们用一个稍微大一点的"abcd" 的例子来充分展示它……第一步很简单;我们将字符串分成大小写字符对:

    [["a", "A"], ["b", "B"], ["c", "C"], ["d", "D"]]
    

    接下来,魔法来了。请注意:

    ["a", "A"].zip(["b", "b"]) == [["a", "b"], ["A", "b"]]
    ["a", "A"].zip(["B", "B"]) == [["a", "B"], ["A", "B"]]
    

    因此,我们可以通过将每个数组的每个元素与当前结果合并(压缩)在一起来开始构建所有可能性的列表:

    ["b", "B"].flat_map { |char| ["a", "A"].zip([char, char])}
      # => [["a", "b"], ["A", "b"], ["a", "B"], ["A", "B"]]
    

    但请注意,我在这里“作弊”了一点,并写了[char, char]。实际上,这个输入需要与当前结果的长度相同。把这些信息结合起来,就得到了上面提到的inject方法:

    inject { |acc, chars| chars.flat_map { |char| acc.zip([char] * acc.count)} }
    

    让我们看看如果我们运行整个方法没有最终的.map(&:join)会发生什么:

      "abcd"
        .chars
        .map { |char| [char, char.swapcase] }
        .inject { |acc, chars| chars.flat_map { |char| acc.zip([char] * acc.count)} }
    
      # => [
        [[["a", "b"], "c"], "d"],
        [[["A", "b"], "c"], "d"],
        [[["a", "B"], "c"], "d"],
        [[["A", "B"], "c"], "d"],
        [[["a", "b"], "C"], "d"],
        [[["A", "b"], "C"], "d"],
        [[["a", "B"], "C"], "d"],
        [[["A", "B"], "C"], "d"],
        [[["a", "b"], "c"], "D"],
        [[["A", "b"], "c"], "D"],
        [[["a", "B"], "c"], "D"],
        [[["A", "B"], "c"], "D"],
        [[["a", "b"], "C"], "D"],
        [[["A", "b"], "C"], "D"],
        [[["a", "B"], "C"], "D"],
        [[["A", "B"], "C"], "D"]
      ]
    

    你有它! join 在一起,你又得到了一个漂亮的简单字符串。

    【讨论】:

    • 嗨,汤姆,感谢您抽出宝贵的时间来贡献这篇文章。我已经读了 3 遍了,但我仍然不明白,哈哈。我会去重新喝咖啡,看看是否有帮助:p
    • 实际上,这里有一个微小的变化,得到了相同的结果,但可能希望使计算过程更容易理解:将.map(&:join)移动到@987654333之后@,而不是放在最后。
    • 我了解所有部分(注入/减少、压缩等),只是它们的组合方式还没有“点击”
    • 假设你有一个任意长度的“部分结果”,例如acc = ["ab", "Ab", "aB", "AB"],并且您想将其与另一个字母“合并”,例如char = "c"。好吧,我用一种有点神秘的方式写了这个:acc.zip([char] * acc.count).map(&:join)。但实际上,再看一遍(我确实说过“可能会被清理”!...),您也可以使用acc.map { |result| result + char } 来实现这一点。
    • 又回到了这个。我明白了:D
    猜你喜欢
    • 2015-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-20
    • 2011-01-16
    • 1970-01-01
    相关资源
    最近更新 更多