せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】【アルゴリズム】文字列の回転。メモリ富豪Ver、メモリ貧乏Ver、再帰Ver、お手玉Ver、回転の回転Ver、

 
■post summary
study about algorithm.the question is "rotation the string".
ex) method("abcdef", 3) -> "defabc"
 

 

問題

文字列の回転を考えます。
method("abcdef", 3) -> "defabc"

 

メモリ富豪

一行で書けます。

def brock_reverse_no1(str, n)
  (str[0,n], str[n, str.length-n] = str[n, str.length-n], str[0,n]).join("")
end
p brock_reverse_no1("abcdef",3)

 

メモリ貧乏

一つずつ要素を入れ替えていくのは、バブルソートの実装をヒントに。

def brock_reverse_no2(str, n)
  n.times do |i|
    (str.length - 1).times do |j|
      str[j], str[j+1] = str[j+1], str[j]
    end
  end
  str
end
p brock_reverse_no2("abcdef",3)

 

再帰

文字列を、回転する要素aとそれ以外b のabに分ける。
a<bの場合、b= b1 + b2 と分ける(b2の長さ = aの長さ)
このとき文字列は a b1 b2 と表現できる。そして、aとb2 を入れ替える。
そうすると、aは正しい場所に来ているので、残りのb2 b1 を同様に入れ替えていく。

考え方は、クイックソート(pivotを使ったソート)を参考に。

def change_same_size_block(start_index, end_index, pivot, str)
  left_block_size = pivot - start_index + 1
  right_block_size = end_index - pivot
  ok = (left_block_size == right_block_size)
  target_size = [left_block_size, right_block_size].min
  str[start_index, target_size], str[-target_size, target_size] = str[-target_size, target_size], str[start_index, target_size]
  return str if ok
  if left_block_size == target_size
    return change_same_size_block(start_index, end_index-target_size, pivot, str) 
  else
    return change_same_size_block(start_index+target_size, end_index, pivot, str) 
  end
end
def brock_reverse_no4(str, n)
  change_same_size_block(0, str.length-1, n-1, str)
end
p brock_reverse_no4("abcdefgh", 6)

 

お手玉

メモリ貧乏Verだと計算量が n*文字列長になってしまう。
隣り合う要素で入れ替えるのではなく、「最終的に正しい場所の要素と交換」することで
メモリ消費を抑え、計算量も文字列長だけになる。

この実装がややこしかったよ。

def brock_reverse_no5(str, quantity)
  change_ch(0, quantity, str)
end
def change_ch(start_index, quantity, str)
  count_change = (str.length - start_index - 1) / quantity
  count_change.times do |i|
    str[start_index + quantity*i], str[start_index + quantity*i + quantity] = str[start_index + quantity*i + quantity], str[start_index + quantity*i]
  end
  last_changed = start_index + quantity*count_change
  next_state = (last_changed + quantity) % str.length
  return str if next_state == 0
  str[last_changed], str[next_state] = str[next_state], str[last_changed]
  return change_ch(next_state, quantity, str)
end
p brock_reverse_no5("0123456789", 3)

 

回転の回転

一番スマートな方法。
左の要素をさかさまにして、右の要素をさかさまにして、最後に全体をさかさまにすると、求める文字列!!

class String
  def _reverse(i, j)
    loop do
      break if i >= j
      self[i], self[j] = self[j], self[i]
      i += 1 ; j -= 1
    end
    self
  end
end
def brock_reverse_no3(str, n)
  str._reverse(0, n-1)._reverse(n, str.length-1)._reverse(0, str.length-1)
end
p brock_reverse_no3("abcdef",3)