コンテンツへスキップ

【Ruby基礎】AtCoder Beginner Contest 003 B – AtCoderトランプ

  • by

■はじめに

Rubyの基礎的な問題をたくさん解くことで基本的な考え方やメソッドの使い方を定着させたい。
基本的にはAtCoderというプログラミングコンテスト(競技プログラミング)の過去問を使う。(AtCoderは難易度が分かれており、難易度の低いA問題かB問題を解いていく)

(5/23時点の方針)
メソッドの切り分け方や値の受け渡しを練習するために、コード長の短さについては気にせずに書くことにする。

(2022/10/17時点の方針)
しばらくはB問題を小さい番号の方からやっていく。たまにA問題もやるかも。

■問題

●出典

AtCoder Beginner Contest 003のB問題
https://atcoder.jp/contests/abc003/tasks/abc003_2

●問題文

AtCoder社では 1 人で行うトランプを使ったゲームが流行っています。
AtCoder社特製トランプでは、各カードにアルファベット小文字 1 文字(az)、または@の文字が書かれています。

ゲームは以下の手順で行います。

  1. カードを同じ枚数ずつ 2 列に並べて文字列を 2 つ作ります。
  2. @のカードは、それぞれa,t,c,o,d,e,rのどれかのカードと置き換えます。
  3. 2 つの列が指し示す文字列が同じであれば勝ち、同じでなければ負けです。

手順 1. で並べられた 2 つの列が指し示す2つの文字列与えられるので、適切に@を置き換えて、このゲームで勝つことができるかどうかを判定するプログラムを書いてください。

●入力

入力は以下の形式で標準入力から与えられる。

S
T
  1. 1 行目には、1 列目のトランプが表す文字列 S が与えられる。
  2. 2 行目には、2 列目のトランプが表す文字列 T が与えられる。
    • S、T ともにアルファベット小文字、および、@のみから構成されることが保証される。
    • S、T の文字数は等しく、1 文字以上、10文字以下であることが保証される。

●出力

このゲームで勝つことが可能であればYou can winと、不可能であればYou will loseと(シングルクォーテーションを除いて)1 行で出力せよ。 また、出力の末尾には改行を入れること。

■回答

●愚直に書く

@a,t,c,o,d,e,rのいずれかに置き換えて同じ文字列にする、ということは、逆に、a,t,c,o,d,e,rの文字が来たら@に変換するプログラムにしてしまった方が比較がしやすいような気がする。

s = gets.chomp
t = gets.chomp
if s.gsub(/a|t|c|o|d|e|r/, "@") == t.gsub(/a|t|c|o|d|e|r/, "@")
  puts "You can win"
else
  puts "You will lose"
end

あれ、これだと通らない…!
今回判定用テストが78個もあって、そのうちの4つだけでWA(wrong answer)になる。
う〜〜〜ん、大筋はあっているけど何か想定できていないパターンがあるのかな…?

標準入力を、1行丸ごと文字列で取るのではなく、1文字ずつの配列に入れて処理してみる。

s = gets.chomp.split("")
t = gets.chomp.split("")

if s.map { |n| n.gsub(/a|t|c|o|d|e|r/, "@")} == t.map { |n| n.gsub(/a|t|c|o|d|e|r/, "@")}
  puts "You can win"
else
  puts "You will lose"
end

これでもWA。。。しかも同じテストが通ってないから、原因としては同じか。

う〜〜〜んわからない。
わからないので他の方の回答を見る。

●他の方の回答例

どうしよう、他の人の回答を見てもわからない。
自分のアプローチは全然出てこないので、やはり何か間違っているのか。

わからな過ぎてググってみたら、AtCoder社長さんの解説があった。
https://www.slideshare.net/chokudai/abc003

自分の回答がダメな理由も書いてあった。
そうか、自分の回答だと例えば

ac
cc

上記の場合本当はloseなのに全部@に変換されてwinになってしまうのか…!

気を取り直して、他の方の回答を元に正答を理解できるように考える。

s = gets.chomp
t = gets.chomp
a = "atcoder"

ans = "You can win"
s.size.times do |i|
  next if s[i] == t[i]
  next if s[i] == "@" && a.include?(t[i])
  next if t[i] == "@" && a.include?(s[i])
  ans = "You will lose"
  break
end
puts ans

以下、コメント付き。

s = gets.chomp # 1行目の標準入力
t = gets.chomp # 2行目の標準入力
a = "atcoder" # 後で@と変換する用

ans = "You can win" # 先にwinをansとして変数に入れとく
s.size.times do |i| # 文字数分だけ繰り返す
  next if s[i] == t[i] # まずsとtが完全一致していたらnextで抜ける
  next if s[i] == "@" && a.include?(t[i]) # sの中に@が含まれていた場合、同じ順番のtの文字が「a/t/c/o/d/e/r」のいずれかの文字であれば、nextで抜ける
  next if t[i] == "@" && a.include?(s[i]) # 1行上の逆バージョンで、tの中に@が含まれていた場合の処理
  ans = "You will lose" # 上記3行のいずれにも該当しなかったら変数ansにloseを入れる
  break # 最後、breakで抜け出す
end
puts ans # ansを出力すればOK

これで通る。
ループさせて抜け出すのとか全然やってなかったな…。

●出てきたメソッド等

公式リファレンスを見る訓練。

■振り返りなど

どうにかわかったような気はするけど、自分でゼロから導き出すのは今の時点では難しい。
A問題的なものに慣れ過ぎてしまったので、今回くらいの問題をきっちり書けるようになっていきたい。