GCJ 2016 Qualification Round B: Revenge of the Pancakes
問題
- と - からなる文字列Sが与えられる。以下の操作を繰り返して、+ のみの文字列に変換するための最小回数を求める。
・文字列Sのi番目までの+と-を反転させて、なおかつ、逆順にする。
例: 文字列Sが ++--+ でi=3の場合、++- を反転(--+)して逆順(+--)にする。 その結果、文字列Sは +---+ になる。
https://code.google.com/codejam/contest/6254486/dashboard#s=p1
方針
操作の制約として、必ず先頭から指定した範囲の文字列を反転させることになるので、最後の操作の直前の状態は、以下のように「-の連続と+の連続」または「-のみ」の状態になる。
----++++ (-の連続と+の連続) -------- (-のみ)
つまり、文字列の前の方から順番に+や-が連続する状態を作っていくようにする。
・先頭が+の場合、順番に文字をチェックしていき、-が出現したら直前までの文字列に対して操作を行う
+++--++ ↓ 先頭が+なので4番目に-を見つけた時点で3番目までを操作対象にする -----++
・先頭が-の場合、上記の逆の操作をする
以上を繰り返すと、文字列の前半が+の連続、-の連続を交互に繰り返すことになり、最終的に+のみになったら処理終了となる。
実装
最初は文字列を実際に反転させる処理を実装し、毎回文字列を先頭からチェックするような実装にしていた。(計算量は O(N2))
しかしながら、動作確認をしていて、i番目までを処理した時点でそれより前の文字列は + or - の連続になっていることは分かっているので、先頭に戻らずそのままi番目以降の文字をチェックすれば良いことに気づいた。(計算量は O(N))
def solve(cakes) n = cakes.size return 0 if cakes.count{|c| c == "+" } == n c = cakes[0] count = 0 i = 1 while(i < n) if cakes[i] != c then count += 1 c = (c == "+" ? "-" : "+") end i += 1 end count += 1 if c == "-" return count end i = 1 STDIN.gets STDIN.each_line do |line| line.chomp! cakes = Array.new line.chars {|c| cakes.push(c)} printf("Case #%d: %d\n", i, solve(cakes)) i += 1 end