コンテンツへスキップ

【Ruby基礎】AtCoder Beginner Contest 018 B – 文字列の反転

■はじめに

Rubyの基礎的な問題をたくさん解くことで基本的な考え方やメソッドの使い方を定着させたい。
基本的にはAtCoderというプログラミングコンテスト(競技プログラミング)の過去問を使う。(AtCoderは難易度が分かれており、難易度の低いA問題かB問題を解いていく)

■問題

●出典

AtCoder Beginner Contest 018のB問題
https://atcoder.jp/contests/abc018/tasks/abc018_2

●問題文

半角の小文字アルファベットのみからなる文字列 S が与えられる。 文字列 S に対して以下の操作 1 から操作 N までを番号の昇順に行う。

  • 操作 i : 左から l_i 番目の文字を左端、左から r_i (1≦l_i<r_i ≦∣S∣) 番目の文字を右端とした部分文字列を逆順にする。

例えば,文字列 abcdef に対して、左から 3 番目の文字 c を左端、左から 5 番目の文字 e を右端とした部分文字列を逆順にすると、文字列 abedcf が得られる。

操作 1 から操作 N までを番号の昇順に行った後の文字列を出力せよ。

●入力

入力は以下の形式で標準入力から与えられる。

S
N
l_1 r_1
l_2 r_2
:
l_n r_n
  • 1 行目には、半角の小文字アルファベットのみからなる文字列 S(1≦∣S∣≦100) が与えられる。
  • 2 行目には、操作の回数を表す整数 N(1≦N≦100) が与えられる。
  • 3 行目から N 行では、操作に関する情報が与えられる。N 行のうち i 行目では、2 つの整数 l_i と r_i (1≦l_i <r_i ≦∣S∣) が空白区切りで与えられる。これは、操作 i が左から l_i 番目の文字を左端、左から r_i 番目の文字を右端とした部分文字列を逆順にする操作であることを表す。

●出力

全操作後の文字列を 1 行に出力せよ。出力の末尾に改行を入れること。

■回答

●愚直に書く

難しい…。まず、標準入力の取り方がこれまでにない感じで時間がかかってしまった。
色々と調べて試行錯誤してやっと下記のように取れた。

s = gets.chomp
n = gets.to_i
lines = readlines.map{|line| line.split(" ").map(&:to_i)}

p s
p n
p lines

これで、下記の標準入力↓

abcdef
2
3 4
1 4

上記が、下記のように出せた。nの数字が変わっても対応可能。

"abcdef"
2
[[3, 4], [1, 4]]

ようやくここからが本番。

2時間くらい試行錯誤したけどどうしてもできなくて断念…。
ダメだったコードは下記。

s = gets.chomp
n = gets.to_i
lines = readlines.map{|line| line.split(" ").map(&:to_i)}

ans = ""
lines.each do |line|
  ans = (line[0]-2 == -1 ? "" : s[..line[0]-2]) + s[(line[0]-1)..(line[1]-1)].reverse + s[line[1]..]
  puts ans
end

# 下記のように、1行ずつの入れ替え結果が列挙される。1行目の入れ替えを保持したまま2行目の入れ替えをしたいんだけどわからない…
# abedcf
# dcbaef

これ以上1人で考えても時間が無駄そうなのでギブアップ。

●リファクタリング/別アプローチ

ギブアップしたのでリファクタリングも無し

●他の方の回答例

いろいろな方の回答を見て、下記で理解できた。

s = gets.chomp
gets.to_i.times do
  l, r = gets.split(' ').map(&:to_i)
  s[l-1..r-1] = s[l-1..r-1].reverse
end
puts s

そうか、s[l-1..r-1] = s[l-1..r-1].reverseだけやれば十分なのか〜。
あと、「標準入力はとにかくまず変数に入れる」がクセになっていたけど今回はそうじゃない方がやりやすそうだった。

●出てきたメソッド等

公式リファレンスを見る訓練。

■振り返りなど

わからないと時間が無限に溶けていくのでギブアップのタイミングは重要だと思った。
この取り組みの場合、30分くらいしたら他の回答例を見ちゃった方が良いかもしれない。