如何在ruby中拆分类似以下示例的字符串?

时间:2016-06-23 02:41:01

标签: ruby string split

我想将任意字符串转换为字符串数组。最好用一个例子来解释转换。如果字符串是"8737928",我想返回以下数组。

#=> ["8737928",
#    "8.737928", "87.37928", "873.7928", "8737.928", "87379.28", "873792.8",
#    "8.7.37928", "8.73.7928", "8.737.928", "8.7379.28", "8.73792.8",
#    "87.3.7928", "87.37.928", "87.379.28", "87.3792.8", "873.7.928",
#    "873.79.28", "873.792.8", "8737.9.28", "8737.92.8", "87379.2.8",
#    "8.7.3.7928", "8.7.37.928", "8.7.379.28", "8.7.3792.8", "8.73.7.928",
#    "8.73.79.28", "8.73.792.8", "8.737.9.28", "8.737.92.8", "8.7379.2.8",
#    "87.3.7.928", "87.3.79.28", "87.3.792.8", "87.37.9.28", "87.37.92.8",
#    "87.379.2.8", "873.7.9.28", "873.7.92.8", "873.79.2.8", "8737.9.2.8",
#    "8.7.3.7.928", "8.7.3.79.28", "8.7.3.792.8", "8.7.37.9.28",  "8.7.37.92.8",
#    "8.7.379.2.8", "8.73.7.9.28", "8.73.7.92.8", "8.73.79.2.8", "8.737.9.2.8",
#    "87.3.7.9.28", "87.3.7.92.8", "87.3.79.2.8", "87.37.9.2.8", "873.7.9.2.8",
#    "8.7.3.7.9.28", "8.7.3.7.92.8", "8.7.3.79.2.8", "8.7.37.9.2.8",
#    "8.73.7.9.2.8", "87.3.7.9.2.8",
#    "8.7.3.7.9.2.8"]

如您所见,在1到6之间的每个索引组合中,在字符串中插入0到6("8737928".size-1 #=> 6)之间的小数点。由于小数点可能跟随或不跟随每个字符。最后,该数组包含2**6 #=> 64个元素。

我该怎么做?

3 个答案:

答案 0 :(得分:3)

def doit(str)
  indices = (1..str.size-1).to_a
  indices.each_with_object([str]) { |ndots, combos|
    indices.combination(ndots).each { |sub| combos << dotify(str, sub) } }
end

def dotify(str, indices)
  indices.reverse.each_with_object(str.dup) { |i,s| s.insert(i,'.') }
end

doit("8737928").size
  #=> 64 

doit "8737928"
  #=> ["8737928",
  #    "8.737928", "87.37928", "873.7928", "8737.928", "87379.28", "873792.8",
  #    "8.7.37928", "8.73.7928", "8.737.928", "8.7379.28", "8.73792.8",
  #    "87.3.7928", "87.37.928", "87.379.28", "87.3792.8", "873.7.928",
  #    "873.79.28", "873.792.8", "8737.9.28", "8737.92.8", "87379.2.8",
  #    "8.7.3.7928", "8.7.37.928", "8.7.379.28", "8.7.3792.8", "8.73.7.928",
  #    "8.73.79.28", "8.73.792.8", "8.737.9.28", "8.737.92.8", "8.7379.2.8",
  #    "87.3.7.928", "87.3.79.28", "87.3.792.8", "87.37.9.28", "87.37.92.8",
  #    "87.379.2.8", "873.7.9.28", "873.7.92.8", "873.79.2.8", "8737.9.2.8",
  #    "8.7.3.7.928", "8.7.3.79.28", "8.7.3.792.8", "8.7.37.9.28",  "8.7.37.92.8",
  #    "8.7.379.2.8", "8.73.7.9.28", "8.73.7.92.8", "8.73.79.2.8", "8.737.9.2.8",
  #    "87.3.7.9.28", "87.3.7.92.8", "87.3.79.2.8", "87.37.9.2.8", "873.7.9.2.8",
  #    "8.7.3.7.9.28", "8.7.3.7.92.8", "8.7.3.79.2.8", "8.7.37.9.2.8",
  #    "8.73.7.9.2.8", "87.3.7.9.2.8",
  #    "8.7.3.7.9.2.8"] 

注意:

dotify("8737928", [1,3,5])
  #=> "8.73.79.28"     

答案 1 :(得分:2)

解决方案1 ​​

经过进一步的反思(见原文,下面可能是不正确的解决方案),看起来OP真正想做的是在字符串中每个可能的位置组合插入点。这是一个字面意思的方法:

def splits(str, prefix="")
  c = str.size - 1
  (0..c).flat_map do |m|
    (0...c).to_a.combination(m).map do |n|
      n.each_with_object(str.dup) {|i,s| s.insert(c-i, ?.) }
    end
  end
end

puts splits("8737928")
# => 8737928
#    873792.8
#    87379.28
#    87379.2.8
#    8737.928
#    8737.92.8
#    8737.9.28
#    8737.9.2.8
#    873.7928
#    873.792.8
#    873.79.28
#    873.79.2.8
#    873.7.928
#    873.7.92.8
#    873.7.9.28
#    873.7.9.2.8
#    87.37928
#    87.3792.8
#    87.379.28
#    87.379.2.8
#    87.37.928
#    87.37.92.8
#    87.37.9.28
#    87.37.9.2.8
#    87.3.7928
#    87.3.792.8
#    87.3.79.28
#    87.3.79.2.8
#    87.3.7.928
#    87.3.7.92.8
#    87.3.7.9.28
#    87.3.7.9.2.8
#    8.737928
#    8.73792.8
#    8.7379.28
#    8.7379.2.8
#    8.737.928
#    8.737.92.8
#    8.737.9.28
#    8.737.9.2.8
#    8.73.7928
#    8.73.792.8
#    8.73.79.28
#    8.73.79.2.8
#    8.73.7.928
#    8.73.7.92.8
#    8.73.7.9.28
#    8.73.7.9.2.8
#    8.7.37928
#    8.7.3792.8
#    8.7.379.28
#    8.7.379.2.8
#    8.7.37.928
#    8.7.37.92.8
#    8.7.37.9.28
#    8.7.37.9.2.8
#    8.7.3.7928
#    8.7.3.792.8
#    8.7.3.79.28
#    8.7.3.79.2.8
#    8.7.3.7.928
#    8.7.3.7.92.8
#    8.7.3.7.9.28
#    8.7.3.7.9.2.8

解决方案2

然而,尽管@ EliSadoff的解决方案并未一概而论,但我确实喜欢他的“想法,即一个时期可能是一个布尔决定。”如果我们考虑字符串中的位置,我们可以在其中插入一个句点作为二进制数 m 中的位,其长度与字符串相同(基数2)相同,我们可以简单地从0开始迭代到2 c -1) -1(其中 c 是字符串的长度)来获得每个可能的这样的数字。例如,如果我们的字符串是"abcd" c = 4),那么我们可以从0迭代到7(2 (4-1) -1)找到每个时期的位置:

 m₁₀ | m₂ 4 2 1 |   4   2   1   | result
─────┼────┴─┴─┴─┼───┴───┴───┴───┼─────────
   0 │    0 0 0 │ a   b   c   d │ abcd
   1 │    0 0 1 │ a   b   c • d | abc.d
   2 │    0 1 0 │ a   b • c   d | ab.cd
   3 │    0 1 1 │ a   b • c • d | ab.c.d
   4 │    1 0 0 │ a • b   c   d | a.bcd
   5 │    1 0 1 │ a • b   c • d | a.bc.d
   6 │    1 1 0 │ a • b • c   d | a.b.cd
   7 │    1 1 1 │ a • b • c • d | a.b.c.d

唯一缺失的部分是根据第二列中的位插入句点。这很简单:为了确定我们是否需要在 n 位置插入一个句点,我们测试 m 中的 n 位是否为1为此,我们可以使用按位运算 m &amp; (1« n )。

将所有内容放在一起,我们得到以下内容:

def splits2(str)
  c = str.size - 1
  (0...2**c).map do |m|
    0.upto(c).with_object(str.dup) do |i,s|
      s.insert(c-i, ?.) if m & (1 << i) > 0
    end
  end
end

解决方案3

只是为了好玩,这是另一个也采用二进制数方法的解决方案,但方式不同。我将把它作为练习留给读者来弄清楚它是如何工作的:

def splits3(str)
  c = str.size - 1
  (0...2**c).map do |m|
    dots = ("%*b" % [c,m]).each_char.map(&{?1=>?.})
    str.each_char.zip(dots).join
  end
end

原始解决方案

与@ CarySwoveland的解决方案类似,但我认为有点简单:

def splits(str, pfx="")
  return [] if str.empty?
  (1...str.size).map {|i| pfx + str.dup.insert(i, ?.) } +
    splits(str[1..-1], "#{pfx}#{str[0]}.")
end

p splits("8737928")
# => [ "8.737928", "87.37928", "873.7928", "8737.928", "87379.28", "873792.8",
#      "8.7.37928", "8.73.7928", "8.737.928", "8.7379.28", "8.73792.8",
#      "8.7.3.7928", "8.7.37.928", "8.7.379.28", "8.7.3792.8",
#      "8.7.3.7.928", "8.7.3.79.28", "8.7.3.792.8",
#      "8.7.3.7.9.28", "8.7.3.7.92.8",
#      "8.7.3.7.9.2.8"
#     ]

答案 2 :(得分:1)

要求不明确,我得出的结果与Cary和Jordan的结果不同:

 def dot_it(prefix, suffix = nil)
   return dot_it(prefix[0], prefix[1..-1]) if suffix.nil? # first call

   (1...suffix.length).flat_map do |i|
     sp, ss = "#{prefix}.#{suffix[0...i]}", suffix[i..-1]
     ["#{sp}.#{ss}", dot_it(sp, ss)].flatten.compact
   end
end
dot_it("8737928")
#⇒ ["8.7.37928", "8.7.3.7928", "8.7.3.7.928", "8.7.3.7.9.28",
#   "8.7.3.7.9.2.8", "8.7.3.7.92.8", "8.7.3.79.28", "8.7.3.79.2.8",
#   "8.7.3.792.8", "8.7.37.928", "8.7.37.9.28", "8.7.37.9.2.8",
#   "8.7.37.92.8", "8.7.379.28", "8.7.379.2.8", "8.7.3792.8",
#   "8.73.7928", "8.73.7.928", "8.73.7.9.28", "8.73.7.9.2.8",
#   "8.73.7.92.8", "8.73.79.28", "8.73.79.2.8", "8.73.792.8",
#   "8.737.928", "8.737.9.28", "8.737.9.2.8", "8.737.92.8",
#   "8.7379.28", "8.7379.2.8", "8.73792.8"]

我的方法给出:

dot_it("8737928").count
#⇒ 31

虽然上述两个答案都给出了21个结果。谁是对的?