コンテンツへスキップ

【Ruby基礎】AtCoder Beginner Contest 028 B – 文字数カウント

■はじめに

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

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

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

■問題

●出典

AtCoder Beginner Contest 028のB問題
https://atcoder.jp/contests/abc028/tasks/abc028_b

●問題文

文字列 S が与えられます。

この文字列は A, B, C, D, E, F の 6 種類の文字から構成されている事が分かっています。

6 種類の文字それぞれについて、S のなかに何回出てくるかを数えてください。

●入力

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

S

1 行に文字列 S(1≦∣S∣≦100) が与えられる。ただし、∣S∣ とは S の長さを表す。また、S は必ず A, B, C, D, E, F の 6 種類の文字から構成されている。

●出力

A, B, C, D, E, F の出現回数を空白区切りで 1 行に出力せよ。出力は標準出力に行い、末尾に改行を入れること。また、行の末尾に余計な空白を入れないよう注意せよ。

■回答

●愚直に書く

まず標準入力を取って、あとは直感としてはtallyを使えそうな気がするので試してみる

s = gets.chomp.split("").sort
p s.tally

これでひとまず、例えば標準入力BEAFの場合に以下のように返ってくるようになる。

BEAF

# => {"A"=>1, "B"=>1, "E"=>1, "F"=>1}

出てこなかった文字(上で言うとCD)が0ということをどうやって出すか…。

色々悩んでいたら、他の超愚直な書き方を思いついたのでとりあえずそちらを作ってみる。

s = gets.chomp.split("").sort
A = s.count('A').to_s
B = s.count('B').to_s
C = s.count('C').to_s
D = s.count('D').to_s
E = s.count('E').to_s
F = s.count('F').to_s

puts A + " " + B + " " + C + " " + D + " " + E + " " + F

通った!
リファクタリングしがいのあるコードができた…笑。

最後、式展開のがまだ良いかな。

s = gets.chomp.split("").sort
A = s.count('A').to_s
B = s.count('B').to_s
C = s.count('C').to_s
D = s.count('D').to_s
E = s.count('E').to_s
F = s.count('F').to_s

puts "#{A} #{B} #{C} #{D} #{E} #{F}"

A〜Fの数字を出すところもto_sが並びまくりなのでなんとかしたい。

カウントして文字列にする部分をメソッド化してみたらどうかな。

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

def count_num(x, y)
  x.count(y).to_s
end

A = count_num(s, 'A')
B = count_num(s, 'B')
C = count_num(s, 'C')
D = count_num(s, 'D')
E = count_num(s, 'E')
F = count_num(s, 'F')

puts "#{A} #{B} #{C} #{D} #{E} #{F}"

良くなったような別に意味のないことをしているような…。
きっともっと短くスパッと書く方法がありそう。

時間切れのため、他の方の回答を見ることにする。

●他の方の回答例

回答を見てすぐに察した。今回の敗因は文字列の範囲を('A'..'F')のようにできることを忘れていたこと。似たようなことを試みようとしたけどうまく書けなくて、もう少し粘ればよかった。

いろいろな回答を拝見して整理したものが下記。

s = gets.chomp
counts = ('A'..'F').map{|c| s.count(c)}
puts counts.join(' ')

●出てきたメソッド等

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

■振り返りなど

下記のようにp ('A'..'F')とかだと6文字出てきてくれないけど、mapとかで回すときはうまく機能するのか。まだまだ理解が足りないことを痛感した。

p ('A'..'F')
# =>"A".."F"

p ('A'..'F').map{|c| c}
# =>["A", "B", "C", "D", "E", "F"]