【发布时间】:2021-08-31 12:07:28
【问题描述】:
给定一个任意的输入字符串,我怎样才能生成一个包含每个可能的大写字符的序列?
例如给定输入 "abc",预期输出将是这 8 个字符串的序列:
ABCABcAbCAbcaBCaBcabCabc
【问题讨论】:
给定一个任意的输入字符串,我怎样才能生成一个包含每个可能的大写字符的序列?
例如给定输入 "abc",预期输出将是这 8 个字符串的序列:
ABCABcAbCAbcaBCaBcabCabc【问题讨论】:
# 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 创建一个枚举器,该枚举器产生具有给定长度的每个可能的位字符串,例如:
[true, true, true][true, true, false][true, false, true][true, false, false][false, true, true][false, true, false][false, false, true][false, false, false]您可以使用输入的字符压缩其中的每一个:
[[true, "a"], [true, "b"], [true, "c"]][[true, "a"], [true, "b"], [false, "c"]][[true, "a"], [false, "b"], [true, "c"]][[true, "a"], [false, "b"], [false, "c"]][[false, "a"], [true, "b"], [true, "c"]][[false, "a"], [true, "b"], [false, "c"]][[false, "a"], [false, "b"], [true, "c"]][[false, "a"], [false, "b"], [false, "c"]]然后,您可以映射这些对中的每一个,并使用排列中的布尔值来决定天气以大写成对的字符,从而为您提供:
["A", "B", "C"]["A", "B", "c"]["A", "b", "C"]["A", "b", "c"]["a", "B", "C"]["a", "B", "c"]["a", "b", "C"]["a", "b", "c"]然后你只需join每个结果,将字符数组转换为字符串。
【讨论】:
char.send(a_case) 等压缩排列。
map。
lazy 不会更改结果值,它只是使它们不会全部同时保存在内存中(这对于像这样的几个字符长的字符串,但对于较长的字符串会是一个问题)。
只是为了好玩,这里有一个 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 在一起,你又得到了一个漂亮的简单字符串。
【讨论】:
.map(&:join)移动到@987654333之后@,而不是放在最后。
acc = ["ab", "Ab", "aB", "AB"],并且您想将其与另一个字母“合并”,例如char = "c"。好吧,我用一种有点神秘的方式写了这个:acc.zip([char] * acc.count).map(&:join)。但实际上,再看一遍(我确实说过“可能会被清理”!...),您也可以使用acc.map { |result| result + char } 来实现这一点。