Ruby 发起一个算法讨论, 有关两个字符串之间逐字符混插

zw963 · 2012年03月16日 · 最后由 jhjguxin 回复于 2012年03月23日 · 5352 次阅读

发起一个算法讨论

问题如下:

一个长字符串: a_long_string = "a very long long string" 一个短字符串: short_string = "SHORT STRING"

要求: 将短字符串, 逐字符的循环插入长字符串中. 并返回修改后的长字符串.

例如: 以上返回结果应该为:

aS HvOeRrTy  SlToRnIgN GlSoHnOgR Ts tSrTiRnIgN





我自己先给一个实现: (脑子卡壳了,多谢二楼 pongyo 的提醒, 汗~~~ )

很明显,我的实现绝对不是最 Rubyful 的. 因为我没有很好的利用 Ruby 自身提供的 强大的字符串 API, 相信一定会有更好地解决方法.

下面是我的代码:

a_long_string = "a very long long string"
short_string = "SHORT STRING"

def insert_str(str1, str2)
  tmp_str = ""
  enum1 = str1.chars
  index = 0
  loop do
    z = enum1.next << str2[index]
    index = (index + 1) % str2.length
    tmp_str << z
  end
  tmp_str
end

insert_str a_long_string, short_string





楼下接上,看看楼下什么想法~

你把 print 语句换成把那个字符放到一个数组或者字符串里就行了呀。


def insert_str long, short
  short_len = short.length
  string_array = long.chars.enum_for(:inject, []).with_index do |(r, char), i|
    r << char
    r << short[i % short_len]
  end
  string_array.join
end

还有一个短版的

def insert_str l,s; l.split("").zip((s * (l.size / s.size) << s[0...(l.size % s.size)]).split("")).join;end

def merge_string(long, short)
  result = []
  long_size = long.size
  i = 0
  short.each_char do |char|
    result << long[i]
    result << char
    i += 1
  end
  result.join('') << long[i..long_size]
end


我的思路很简单。

额 抛砖引玉。。

def insert_str long, short
  short_arr = short.split("")
  result = ""
  long.split("").each{|str| result << str + short_arr.first and short_arr.rotate!}
  return result
end



a_long_string = "a very long long string"
short_string = "SHORT STRING"

def insert_str(str1, str2)
  ls, ss = [str1, str2].map(&:chars)
  ls.zip(ss.cycle).join
end


def insert_str(long, short)
  div, mod = long.size.divmod(short.size)
  c = short * div + short[0..(mod - 1)]
  long.chars.zip(c.chars).join
end

#2 楼 @pongyo , 谢谢. 脑子卡壳了. 平常总是用数组, 换作字符串了, 没反应过来. 哈哈.

不过你的算法, 貌似好难看懂....

#3 楼 @daqing 你的代码不满足我的要求.

因为你的短字符串只迭代了一次. 下面是你的输出:

"aS HvOeRrTy  SlToRnIgN Glong string"

#4 楼 @huyong36 Cool~~

之前, 我都不知道有 rotate 这个 API.

#5 楼 @hysios 太酷了~~ 我倒是想到了用 cycle, 但是没想到可以这样用 each_with_object

#6 楼 @hooopo , 无语了. 竟然用这种办法.....

来个性能最差版

def insert_str(s1,s2)
    s1.chars.zip((s2*(s1.length/s2.length+1)).chars).join
end


这个可能要好点

def insert_str2(s1,s2)
    s1.chars.inject('') {|ss,s| ss << s + s2[(ss.length/2)%s2.length]}
end

@hysios 的代码很棒阿,使用 api 特别恰当,学习一下

我认为易读的版本

def insert(long,short)
  enum_l = long.each_char
  enum_s = short.each_char.cycle
  temp = ""
  loop do
    temp << enum_l.next << enum_s.next
  end
  long.replace(temp)
end


#5 楼 @hysios 五楼修改后的代码, 堪称把 Ruby 简约而不失魔幻的风格堪称发挥到了极致. 代码量很少, 而且看起来相当直观. 绝对是自文档代码的典范.

尤其是那个: map(&:chars), 我也很喜欢用, 甚至我用 inject 时, 有时候都加上 了&, inject(&:method), 不过有性能损失. 可惜, 迭代器中, 能有此种用法的, 貌似也就 inject 和 map 而已.

另外, cycle 直接作为 zip 的参数被传递, 神来之笔.

#14 楼 @FenRagwort

很明显, 你的和我的如出一辙, (标题贴),不过你的更好. 哈哈

#12 楼 @hhuai

第一个算法: 又是乘法, 又是除法, 而且, 你这个算法对于 zip 也有要求, 那就 是必须多余的字符被抛弃, 否则结果肯定不对. 真有你的. 这都能想到.

第二个算法: 我咋就看不懂呢?

我用手机上网,没法运行代码验证,不过我怀疑楼上用 zip 再 join 的方式能成功吗?zip 以后得到的数组,其元素也是一个数组,如 [['a','S'],[' ','H'], ...] 这样。这不可以直接 join 吧,还得 flatten 一下才行。

#18 楼 @FenRagwort

可行的. 多级字符串数组, 会自动首先 flatten, 然后再 join, 我也是试过才知道的.

#5 楼 @hysios cycle 不错。

#15 楼 @zw963 zip + cycle 才是经典

我觉得 map 那句是多余呀 ,修改一下 @hysios 的:-)

def insert_str(str1, str2)
  str1.chars.zip(str2.chars.cycle).join
end

@hooopo 实际上我之前看错了需求 原始语句是这样的

def insert_str(str1, str2)
  short, long = [str1, str2].sort.map(&:chars)
  ...
end


#22 楼 @hooopo

zip + cycle 才是经典

的确经典, 之前没想过可以这样用, 因为这个我专门查了搞头书, 才知道, zip 的参数, 首先会隐式的被转化为数组, 然后才执行混插操作.

#24 楼 @zw963 忍不住再回复一下,看来象 string 和 list 这样的基础 api 还要深挖,这个例子绝对适合拿出来 show 一下 ruby style

zip + cycle 真是经典,过瘾看着。

我也来个,不用 enumerator 的

def insert_str(str1, str2) i = -1 str1.split(//).join("\0").gsub("\0") do i = (i + 1) % str2.length str2[i] end end

不好意思,忘用代码风格了

def insert_str(str1, str2)
  i = -1
  str1.split(//).join("\0").gsub("\0") do
    p $&
    i = (i + 1) % str2.length
    str2[i]
  end
end


def insert_cycle_char long, short         
    long.split('').zip(short.split('').cycle).flatten.join       
end

insert_cycle_char "a very long long string" , "SHORT STRING"


#28 楼 @geekontheway 这个比 @hysios 的差一些:chars 比 split 的语义更清晰,而且很可能性能更好,flatten 在 join 之前用是多余的

#29 楼 @fsword 果然如此,受教了

呃……来一个思路简单版的 把字串看成数组 两个两个往结果里加……

def insert_str2(str1,str2)
  result = ''
  str1.length.times {|n|result << str1[n] << str2[n % str2.length] }
  result
end

发布这个帖子, 让我受益匪浅.

#32 楼 @hegwin 虽然实现细节和我差不多, 不过你这个想法, 太奇妙了

在来复杂地,纯属恶搞了,别介意。

递归版的

def insert_str(long, short, index = 0)
  long[index] ? long[index] << short[index % short.length] << insert_str(long, short, index + 1) 
                  : ""
end

fiber 版的:

def insert_str(long, short)
  require 'fiber'

  longer = Fiber.new do |shorter, result|
    long.each_char do |char|
      result << char
      shorter.transfer(longer, result)
    end
    result
  end

  shorter = Fiber.new do |longer, result|
    short.each_char.cycle do |char|
      result << char
      longer.transfer(shorter, result)
    end
  end

  longer.transfer(shorter, "")  
end

笨办法 容易看懂 哈 嘿嘿

a_long_string = "a very long long string"
short_string = "SHORT STRING"

result=""
i = 0

a_long_string.each_char do |a|
  i=0 if i == short_string.length
  result << a << short_string[i]
  i += 1
end
result


需要 登录 后方可回复, 如果你还没有账号请 注册新账号