Google code jam 2012 qualification round A. Speaking in tonguesに挑戦する.
この問題を入力と出力で定義すると,
である.
なお,Google語は英語のアルファベットを入れ替えただけである.
これだけだと,Google語がなんだかわからないので,解きようがない.
しかし,入力と出力の例が以下の通り与えられている.
よって,この例からGoogle語と英語の辞書(略してグー英辞書)を作る.
手作業でもグー英辞書は作れるが,できればプログラミングでどうにかしたい.
これまでとは異なり,いきなり入力データを読み込んで,グー英辞書を作ることにする.
グー英辞書を返す関数google_english_dictionaryを以下に定義する.
def google_english_dictionary(input_file_name, output_file_name):
input_file = open(input_file_name)
input_data = input_file.readlines()
input_file.close()
output_file = open(output_file_name)
output_data = output_file.readlines()
output_file.close()
T = int(input_data.pop(0))
ge_dic = {}
for t in range(T):
google_string = input_data.pop(0).rstrip()
english_string = output_data.pop(0).split(':')[1]
english_string = english_string.strip()
for i in range(len(google_string)):
ge_dic[google_string[i]] = english_string[i]
return ge_dic
早速,問題のページの入力サンプルをファイルA-sample.inとして,出力サンプルをファイルA-sample.outとして用意し,make_google_english_dictionaryを実行する.
ge_dic = google_english_dictionary('A-sample.in', 'A-sample.out')
そして,出来上がったグー英辞書を確認する
ge_dic
{'e': 'o', 'j': 'u', 'p': 'r', ' ': ' ', 'm': 'l', 'y': 'a', 's': 'n', 'l': 'g', 'c': 'e', 'k': 'i', 'd': 's', 'x': 'm', 'v': 'p', 'n': 'b', 'r': 't', 'i': 'd', 'b': 'h', 't': 'w', 'a': 'y', 'h': 'x', 'w': 'f', 'f': 'c', 'o': 'k', 'u': 'j', 'g': 'v'}
アルファベット順に並んでいる方が見やすいので,アルファベット順に表示する.
アルファベット順に表示するために辞書のメソッドと組み込み関数を新たに紹介する.
辞書のメソッドkeys()を使うと,辞書のキーの一覧を得られる. 戻り値はdict_keysという型である. dict_keysはリストのように扱える型である.
ge_dic.keys()
dict_keys(['e', 'j', 'p', ' ', 'm', 'y', 's', 'l', 'c', 'k', 'd', 'x', 'v', 'n', 'r', 'i', 'b', 't', 'a', 'h', 'w', 'f', 'o', 'u', 'g'])
組み込み関数sorted()を使うと,引数のリスト(あるいはそれっぽいもの)を昇順に並べ替えたリストが返される.
sorted(ge_dic.keys())
[' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y']
keys()とsorted()を使って,ge_dicの内容を表示してみる. 以下ではフォーマット済み文字列リテラルの中でシングルクォーテーションを使っているので,一番外側はダブルクォーテーションで囲っている.
for k in sorted(ge_dic.keys()):
print(f"'{k}': '{ge_dic[k]}'")
' ': ' ' 'a': 'y' 'b': 'h' 'c': 'e' 'd': 's' 'e': 'o' 'f': 'c' 'g': 'v' 'h': 'x' 'i': 'd' 'j': 'u' 'k': 'i' 'l': 'g' 'm': 'l' 'n': 'b' 'o': 'k' 'p': 'r' 'r': 't' 's': 'n' 't': 'w' 'u': 'j' 'v': 'p' 'w': 'f' 'x': 'm' 'y': 'a'
このグー英辞書にはqとzがない. 改めてサンプル入力とサンプル出力を見ると,確かにqとzがない.
これではグー英辞書を完成できないと思われる. しかし,問題をよく読むと「例えば……'z'->'q'」と書いてある.
残りの'q'は'z'しか割り当てる先がないので,合わせて辞書に加える.
ge_dic['z'] = 'q'
ge_dic['q'] = 'z'
for k in sorted(ge_dic.keys()):
print(f"'{k}': '{ge_dic[k]}'")
' ': ' ' 'a': 'y' 'b': 'h' 'c': 'e' 'd': 's' 'e': 'o' 'f': 'c' 'g': 'v' 'h': 'x' 'i': 'd' 'j': 'u' 'k': 'i' 'l': 'g' 'm': 'l' 'n': 'b' 'o': 'k' 'p': 'r' 'q': 'z' 'r': 't' 's': 'n' 't': 'w' 'u': 'j' 'v': 'p' 'w': 'f' 'x': 'm' 'y': 'a' 'z': 'q'
これで辞書が完成したので,問題の入力をファイルとして読み込んで,正しい出力をファイルに出力する関数answerを定義する. この関数answerは,グー英辞書も引数として使うことにする.
def answer(input_file_name, ge_dic, output_file_name):
input_file = open(input_file_name)
input_data = input_file.readlines()
input_file.close()
output_file = open(output_file_name, 'w')
T = int(input_data.pop(0))
for t in range(T):
google_string = input_data.pop(0).rstrip()
english_string = ''
for g in google_string:
english_string += ge_dic[g]
output_file.write(f'Case #{t+1}: {english_string}\n')
output_file.close()
return
データが書き込まれているテキストファイルA-sample.inを用意して,それを読み込み,A-sample.outというファイルに出力を書き込んでみる.
answer('A-small-practice.in', ge_dic, 'A-small-practice.out')
望みどおりの文字列がA-sample.outに書き出されているか,テキストエディターで見てみよう.
上記の関数make_google_english_dictionaryでは,入力データと出力データを同時に見比べてグー英辞書を作っている. それぞれ同じ長さの文字列であることがわかっているので添字iで反復したが,もっとスマートにコードを書く方法がある.
それは組み込み関数zipを使う方法である. まず,以下に組み込み関数zipの使用例を挙げる.
X = [1, 2, 3]
Y = [100, 1000, 10000]
for x, y in zip(X, Y):
print(x + y)
101 1002 10003
ここから想像がつくと思うが,zipは複数のリストを引数として,反復などにおいてそれぞれの値を同時に返す関数である.
今一度,zipの使用例を挙げる.
list(zip(X, Y))
[(1, 100), (2, 1000), (3, 10000)]
これでさらに雰囲気がわかったと思う.
リストの中の()はタプルである. タブルはリストとほどんど同じである. 異なるのは「要素を変更できない」ということだけである. タプルに関しては,いずれ改めて説明する.
組み込み関数zipを用いて,google_english_dictionaryを再定義する. ついでに,'z'と'q'も予めコードに入れておく.
def google_english_dictionary(input_file_name, output_file_name):
input_file = open(input_file_name)
input_data = input_file.readlines()
input_file.close()
output_file = open(output_file_name)
output_data = output_file.readlines()
output_file.close()
T = int(input_data.pop(0))
ge_dic = {}
for t in range(T):
google_string = input_data.pop(0).rstrip()
english_string = output_data.pop(0).split(':')[1]
english_string = english_string.strip()
for g, e in zip(google_string, english_string): # ここがzipを使っている行
ge_dic[g] = e # zipの利用によって,繰り返し文の中がスッキリしている.
ge_dic['z'] = 'q'
ge_dic['q'] = 'z'
return ge_dic
ge_dic = google_english_dictionary('A-sample.in', 'A-sample.out')
for k in sorted(ge_dic.keys()):
print(f"'{k}': '{ge_dic[k]}'")
' ': ' ' 'a': 'y' 'b': 'h' 'c': 'e' 'd': 's' 'e': 'o' 'f': 'c' 'g': 'v' 'h': 'x' 'i': 'd' 'j': 'u' 'k': 'i' 'l': 'g' 'm': 'l' 'n': 'b' 'o': 'k' 'p': 'r' 'q': 'z' 'r': 't' 's': 'n' 't': 'w' 'u': 'j' 'v': 'p' 'w': 'f' 'x': 'm' 'y': 'a' 'z': 'q'