AtCoder の ABC219C の問題は以下のリンクから参照下さい。
簡単にいうと、アルファベットの順番を替えた上で、複数の文字列を辞書順にソートして出力するという問題です。
じつはこれ、とても Ruby らしい簡潔な解法が存在するので、まずはそちらを説明します。
x = gets.chomp
n = gets.to_i
strs = n.times.map { gets.chomp }
puts strs.sort_by { _1.tr(x, "a-z") }
x
は新しいアルファベットの順番、strs
はソートすべき文字列の入った配列で、sort_by
を使って、解いている部分は1行で書けています。かかる時間もかなり速いです。
本題
さて、Array#sort
は配列の中身が文字列の場合、暗黙にいわゆる「宇宙船演算子」、つまりString#<=>
を使って辞書順にソートします。では、これを再定義してやれば、ただのArray#sort
でいけるのではないかという、変態的発想をもちました。
x = gets.chomp
n = gets.to_i
strs = n.times.map { gets.chomp }
$table = x.each_char.map.with_index { |c, i| [c, i] }.to_h
class String
def <=>(other)
l = [length, other.length].min
(0...l).each do |i|
a = self[i]
b = other[i]
if a != b
return $table[a] <=> $table[b]
end
end
length <=> other.length
end
end
puts strs.sort
「辞書順」の定義は、問題文の「辞書順とは?」にあるアルゴリズムをそのまま実装しました。グローバル変数なぞ使って、美しくないコードですね。時間も上の簡潔な解法に比べ、10倍ほどかかっています。メリットはまるでないのですが、オープンクラスを使い、組み込みの比較用のメソッドを再定義するという、極め付きの動的言語である Ruby 以外ではちょっと不可能な解法ではないでしょうか。実際、ソートはただのArray#sort
で済んでいますね。お粗末様でした。