ABC211 B問題 Python解説

AtCoder Beginner Contest 211 B-Cycle Hit

問題はこちら

Pythonでの解法】

まず解答例を下に示します。

S = [input() for _ in range(4)]
hits = ["H", "2B", "3B", "HR"]
flag = True

for i in hits:
  if i not in S:
    flag = False
    break

print("Yes" if flag else "No")

1. 問題文の把握

文字列 S_iはH,2B,3B,HRのどれかであり、 S_1から S_4までにH,2B,3B,HRが揃っているか?(1つずつ存在しているか?)を判定する問題です。
野球のルールを知っている方ならタイトル"Cycle Hit"から、理解が容易かもしれません。問題文に明記はないものの、Hはヒット、2Bはツーベースヒット(二塁打)、3Bは スリーベースヒット(三塁打)、HRはホームラン(本塁打)を指していると思われます。

2. コードを考える

(0) 標準入力
本問では、改行区切りで文字列が与えられます。そこで、input().split()は使えません。与えられる文字列は4つと決まっているため、各行の文字列をinput()で受け取るという処理を4回行えば文字列全てを得ることができます。ですので、最も原始的な方法は次の通りです。

#一番原始的な方法
S1 = input()
S2 = input()
S3 = input()
S4 = input()

しかし、折角繰り返し処理の書き方を知っている訳ですから、使わない手はありません。上のコードをfor文で書き換えようとするとき、初心者が最初に思いついてうっかり書いてしまいそうな間違ったコードを下に示します。

#間違ったコード
for i in range(4):
  S = input()

確かにこれはinput()を4回繰り返す処理です。しかし、これでは標準入力で受け取る度に変数Sに格納される文字列が更新されてしまい、繰り返し処理が終わった時には  i = 3 の時の文字列( S_4)しか残っていません。つまり  S_1,  S_2,  S_3 がどこにも記憶されず、後々処理に使えません。
そこで、標準入力で受け取った文字列を記憶させる場所を用意する必要があります。それがリストです。リストを使うと次のような書き方になります。

#書き方その1
S = []
for i in range(4):
  hit = input()
  S.append(hit)
#ラスト2行をまとめてS.append(input())と書いても同じ

まず空のリストSを用意しておきます。その後、「標準入力で文字列( S_i)を変数hitに格納した上でhitをappend()でリストSの要素として加える」という処理をfor文を用いて4回繰り返しています。これにより、リストSに標準入力で与えられるデータ全てを記憶させることができます。
基本的にはこれで良いのですが、解答例ではこの処理を1行にひとまとめにしています。それが解答例の1行のコードです。

#書き方その2
S = [input() for _ in range(4)]

リスト内包表記という書き方を用いて4行(3行)を1行に短縮しています。内包表記の詳細については割愛するので各自調べ頂きたいのですが、[]の中に標準入力の受け取り処理input()とfor文を書くことで、書き方その1と同じように改行区切りの文字列をそれぞれ要素に持つリストを作ることができます。(繰り返し変数に代入することはないため、_を用いています。書き方その1でも_で問題ありません)
今回の解答例では、この書き方その2を採用しております。長くなりましたが、標準入力の説明は以上になります。
(1) 方針と下準備
与えられた4つの文字列(リストSに格納した文字列群)にH, 2B, 3B, HRが1つずつあるかを調べたいので、予めH, 2B, 3B, HRを要素に持つリストhitsを用意して、このリストhitsの要素がそれぞれリストSにもあるかを調べていくことにします。
さらに、H, 2B, 3B, HRが1つずつあったかどうかを記憶させる変数flagも用意しておきます。(出力する時の判断基準的役割です)この時、ブール値を用いてflagをTrueとしておきます。(4つ全て揃っている前提としておき、もしそうでなければflagをFalseに変える方針でいきます)
ここまでのコードは次の通りです。

S = [input() for _ in range(4)]
#以下が新規追加分
hits = ["H", "2B", "3B", "HR"]
flag = True

(2) 根幹
for文を用いてリストhitsの要素(H, 2B, 3B, HR)を取り出し、リストSにあるかどうかを調べます。 この時、予めflag = True としたので、リストSになかった場合にflagを変えるため、not in を用いてif文を書きます。 また、flagがFalseになった時点でfor文を繰り返す必要がなくなるため、ここではbreak も書いています。

S = [input() for _ in range(4)]
hits = ["H", "2B", "3B", "HR"]
flag = True
#以下が新規追加分
for i in hits:
  if i not in S:
    flag = False
    break

(3) 出力
最後はお馴染みの三項演算子を用いて、flagがTrueならYesFalseならNoを出力します。この時点でflagがTrueということは、if文の条件式が一度も成立しないままfor文が終わったことを意味します。つまり、H, 2B, 3B, HRの4つ全てが与えられた文字列群に揃っていたということになります。

S = [input() for _ in range(4)]
hits = ["H", "2B", "3B", "HR"]
flag = True

for i in hits:
  if i not in S:
    flag = False
    break
#以下が新規追加分
print("Yes" if flag else "No")

これで解答例が完成しました!

3. まとめ

お疲れ様でした。いかがでしたでしょうか?今回はリスト内包表記を用いるに至るまでの標準入力の説明もあり、これまでの最長の記事となってしまいました。データの与えられ方はいつもスペース区切りではありません。改行区切りにもちゃんと対応できるようにしておく必要があります。
それ以外の文法はif文、for文がメインとなっているため、そこまで難しくはなかったかもしれません。
それでは、次回の記事もお楽しみに!(次回は211 B問題の別解をご紹介します!別解はたったの2行で書き終えてしまう方法となっています!是非そちらもご覧下さい)