CodeIQ:正規表現で暗号を解こう!エニ熊の暗号文

私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「正規表現で暗号を解こう!エニ熊の暗号文」(CodeIQ)を参照してください。

問題の概要

標準入力から1行で暗号文が渡されます。
正規表現を使って暗号文を解読し、結果の文字列を出力してください。
規則は次のようになります。
・暗号文の先頭から「wbw」の様に同じ文字で挟まれた文字を探す
・これを「wbw」→「b」に置換する。
・この処理を置換不能になるまで繰り返す。

【例】
wbwvcvhczhdzmdpmyjnjpnsesiyiulugxgflftotaokqkxqr
bvcvhczhdzmdpmyjnjpnsesiyiulugxgflftotaokqkxqr
bchczhdzmdpmyjnjpnsesiyiulugxgflftotaokqkxqr
bhzhdzmdpmyjnjpnsesiyiulugxgflftotaokqkxqr
bzdzmdpmyjnjpnsesiyiulugxgflftotaokqkxqr
bdmdpmyjnjpnsesiyiulugxgflftotaokqkxqr
bmpmyjnjpnsesiyiulugxgflftotaokqkxqr
bpyjnjpnsesiyiulugxgflftotaokqkxqr
bpynpnsesiyiulugxgflftotaokqkxqr
bpypsesiyiulugxgflftotaokqkxqr
bysesiyiulugxgflftotaokqkxqr
byeiyiulugxgflftotaokqkxqr
byeyulugxgflftotaokqkxqr
beulugxgflftotaokqkxqr
belgxgflftotaokqkxqr
belxflftotaokqkxqr
belxltotaokqkxqr
bextotaokqkxqr
bexoaokqkxqr
bexakqkxqr
bexaqxqr
bexaxr
bear

私のプログラム

Rubyで解答しています。

#!/usr/bin/ruby

def solve(s)
	if s.sub!(/(.)(.)\1/, '\2') == nil then return s
	else solve(s)
	end
end

while line = gets
	line.strip!
	if line.empty? then
		next
	end

	puts solve(line)
end

解説

問題の通りに実装するだけです。
ワンライナーでかけるだろうなと思いましたが再帰で繰り返しました。

変換

sub()は第一引数の正規表現パターンに一致した最初の部分を1回だけ第二引数の指定に従って置換する関数です。パターンマッチは「(.)(.)\1」で「.」が任意の一文字にマッチし「\1」は最初の「(.)」を参照するので同じ文字で挟まれた文字にマッチします。
そして、置換後の文字列は「\2」なので二つ目の「(.)」、つまり挟まれた文字になるので問題通りの置換ができます。

上記で1回だけの置換ができるので置換後の文字列を再帰的にsolve()に渡すことで繰り返します。繰り返しを終了するのはパターンマッチに失敗した時で、この時の結果を返します。

雑感

この問題は解説されていますが、ショートコーディングの問題でもあったようです。もう少しはっきりと書いてくれてあればもう少し頑張ったのに。
それから、これをやったのはRubyを始めて間もない頃だったのですが、この問題でRubyは最後に評価された式が暗黙的にreturnされるということを知りました。