こんにちは!じゃいごテックのあつしです。
今回はpaiza Bランクレベルアップメニュー から文字と整数の組のソート2という問題を解説します。
この問題集は項目と値がペアになっているデータの集計とソートに関する6個のSTEP問題(D級)とFINAL問題(C級)で構成されていて、STEP問題を解いて行けばFINAL問題も解けるはず!という構成となっています。
STEP問題を解いてみる
STEP問題は基本的なメソッドについての問題です。
簡単な解説は付けていますが、難しいと感じたら下記の記事も参考にしてみて下さい。
※過去に出題された小問題が出てくることがありますが、チケットに余裕があれば復習のつもりで取り組んでみましょう。
STEP1: インクリメント (paizaランク D 相当)
STEP1 は受け取った整数をインクリメント(+1)して出力する問題です。
解答例
<< EOS 入力例1 -10 出力例1 -9 入力例2 0 出力例2 1 入力例3 1 出力例3 2 EOS n = gets.to_i puts n + 1
解答例: 標準入力で受けとった文字列を整数型に変換、+1 して出力しています。
STEP2: 重複の判定 (paizaランク D 相当)
STEP2 は指定された配列["HND", "NRT", "KIX", "NGO", "NGO"]
の要素が重複しているかを調べる問題です。
解答例
ary = ["HND", "NRT", "KIX", "NGO", "NGO"] # [解答例1] len = ary.length duplicate = false (0..len - 1).each do |i| (i + 1..len - 1).each do |j| if ary[i] == ary[j] duplicate = true break end end break if duplicate end if duplicate puts "true" else puts "false" end # [解答例2] hash = ary.group_by(&:itself).map { |key, val| [key, val.length] }.to_h puts hash.values.max > 1 ? "true" : "false" # [解答例3] puts ary.tally.values.max > 1 ? "true" : "false"
解答例1: 配列の先頭要素から順に、続く要素と同じものがあるかを判定して、同じものがあればループを抜ける処理を行なっています。
解答例2: group_byメソッド
で要素ごとに配列をまとめたハッシュを作成し、値の配列の中で最大値を調べています。(解答例3と合わせるために要素を集計したハッシュに戻しています。)
解答例3: tallyメソッド
で要素ごと集計したハッシュを作成し、値の配列の中で最大値を調べています。
STEP3: 配列(リスト)の重複カウント (paizaランク D 相当)
STEP3 は指定された配列["HND", "NRT", "KIX", "NGO", "NGO", "NGO", "NGO", "NGO"]
の重複している要素を数える問題です。
※ 重複している要素は1種類であることがわかっています。
解答例
# [解答例1] len = ary.length count = 1 (0..len - 1).each do |i| (i + 1..len - 1).each do |j| if ary[i] == ary[j] count += 1 end end break if count > 1 end puts count # [解答例2] sorted_ary = ary.group_by(&:itself).map { |k, v| [k, v.length] }.sort_by { |k, v| v }.reverse p sorted_ary[0][1] # [解答例3] sorted_ary = ary.tally.sort_by { |k, v| v }.reverse puts sorted_ary[0][1]
解答例1: 配列の先頭要素から順に、続く要素と同じものがあるかを判定して、同じものがあればカウントアップしていき、カウントアップ終了後ループを抜けて出力しています。
解答例2: group_byメソッド
とmapメソッド
で要素の出現回数を集計した配列を作って、sort_byメソッド
とreverseメソッド
で出現回数が多い順(降順)で並び替えて先頭の2番目の要素(最大の出現回数)を出力しています。
解答例3: tallyメソッド
で要素ごと集計したハッシュを作成し、sort_byメソッド
とreverseメソッド
で出現回数が多い順(降順)で並び替えて先頭の2番目の要素(最大の出現回数)を出力しています。
STEP4: 配列のソート (paizaランク D 相当)
STEP4 は指定された配列[1, 3, 5, 6, 3, 2, 5, 23, 2]
を昇順に並び替えて出力する問題です。
解答例
ary = [1, 3, 5, 6, 3, 2, 5, 23, 2] # [解答例1] sorted_ary = [] ary.each do |t_n| inserted = false sorted_ary.each_with_index do |a_n, idx| if t_n < a_n sorted_ary.insert(idx, t_n) inserted = true break end end sorted_ary << t_n if not inserted end puts sorted_ary # [解答例2] puts ary.sort
解答例1: 配列ary
の先頭から順に値を取り出し、sorted_ary
の値と比較して昇順になるようにsorted_ary
に挿入しています。
※ 解説のために自前でソートを実装しましたが、通常はsortメソッド
を使った方が楽なうえに処理も早いです。
解答例2: sortメソッド
で昇順の配列を生成して出力しています。
STEP5: 数字のみの出力 (paizaランク D 相当)
STEP5 は複数の半角スペース区切りの文字と整数のペアが渡され、整数だけを先頭から順に出力する問題です。
解答例
<<"EOS" 4 S 1 F 2 E 5 Y 6 出力例1 1 2 5 6 入力例2 3 S -3 G -5 R -35 出力例2 -3 -5 -35 入力例3 6 A 12 E 42 G -8 R -7 Y 56 E 22 出力例3 12 42 -8 -7 56 22 EOS # [解答例1] n = gets.to_i ary = [] n.times do s, d = gets.split ary.push([s, d.to_i]) end ary.each do |s, d| puts d end # [解答例2] n = gets.to_i hash = {} n.times do s, d = gets.split hash[s] = d.to_i end puts hash.values
解答例1: 文字と数字のペアを配列で受け取って、二次元配列の先頭から順に2番目の要素を出力しています。
解答例2: 文字と数字のペアを配列で受け取り、文字をキー、数字を値にしたハッシュに格納して、valuesメソッド
で値の配列を取得して出力しています。
STEP6: 昇順ソート出力 (paizaランク D 相当)
STEP6 は半角スペース区切りの整数を受け取り、昇順に並べ替えて出力する問題です。
解答例
<<"EOS" 入力例1 8 90 777 8888 121 333 4 29 2 出力例1 2 4 29 90 121 333 777 8888 入力例2 10 10 9 8 7 6 5 4 3 2 1 出力例2 1 2 3 4 5 6 7 8 9 10 入力例3 1 3 出力例3 3 EOS # [解答例1] n = gets.to_i ary = gets.split.map(&:to_i) sorted_ary = [] ary.each do |t_n| inserted = false sorted_ary.each_with_index do |a_n, idx| if t_n < a_n sorted_ary.insert(idx, t_n) inserted = true break end end sorted_ary << t_n if not inserted end puts sorted_ary # [解答例2] n = gets.to_i ary = gets.split.map(&:to_i) puts ary.sort
解答例1: 配列ary
の先頭から順に値を取り出し、sorted_aryの値と比較して昇順になるようにsorted_aryに挿入しています。
※ 解説のために自前でソートを実装しましたが、通常はsortメソッドを使った方が楽なうえに処理も早いです。
解答例2: sortメソッドで昇順の配列を生成して出力しています。
文字と整数の組のソート2 (paizaランク C 相当)を解いてみる
※ paiza Bランクレベルアップメニューより
問題
1行目に行数を表す整数 n、続く n 行の各行で「文字」と「整数」の組が空白区切りで入力されます。
n 個の組を、「整数」の値で昇順に並べ変え、「文字」を出力してください。
入力される値
入力は以下のフォーマットで与えられます。
n
S_1 D_1
S_2 D_2
...
S_i D_i
...
S_n D_n
S_i は「文字」で、D_i は「整数」です。
入力される文字 S_i は、同じ文字が複数入力されることはなく、整数 D_i も同様です。
入力値最終行の末尾に改行が1つ入ります。
期待する出力
n 個の組を、「整数」の値で昇順に並べ変え、「文字列」を1行ずつ出力してください。
条件
すべてのテストケースにおいて、以下の条件をみたします。
- 1 ≦ n ≦ 100
- -10,000 ≦ D_i ≦ 10,000
- S_iは半角英文字1文字
- iとjが異なるなら、D_iとD_jは異なる
- iとjが異なるなら、S_iとS_jは異なる
- 入力例1
- 2
A 1
B 2
- 出力例1
- A
B
- 入力例2
- 3
G 0
S 3
E -2
- 出力例2
- E
G
S
- 入力例3
- 5
H -2
R 0
W -5
A -1
E -20
- 出力例3
- E
W
H
A
R
攻略ポイント
ポイント
- 未登録の文字列(キー)なら値とセットで保持し、登録済みの文字(キー)なら整数の値を足し合わせる
- sort_byメソッドで二次元配列(又はハッシュ)の降順ソートを行う
- 二次元配列(又はハッシュ)の要素を半角スペースで連結して出力する
デバッグを楽にするためにメソッドを作成します。メソッドについては下記の記事も参考にしてみて下さい。
問題を解く流れ
入出力例をコピペしてヒアドキュメントで変数に代入し、データを確認します。正しく受け取れていれば確認用コードは削除します。
INPUT1 = <<~"EOS" 7 A 1 D 6 C 2 G 4 B 70 A 10 B 5 EOS OUTPUT1 = <<~"EOS" B 75 A 11 D 6 G 4 C 2 EOS INPUT2 = <<~"EOS" 3 G 0 S 3 E -2 EOS OUTPUT2 = <<~"EOS" S 3 G 0 E -2 EOS INPUT3 = <<~"EOS" 5 A -2 E 0 W -5 A -1 E -20 EOS OUTPUT3 = <<~"EOS" A -3 W -5 E -20 EOS p INPUT1 # > "7\nA 1\nD 6\nC 2\nG 4\nB 70\nA 10\nB 5\n" p OUTPUT1 # > "B 75\nA 11\nD 6\nG 4\nC 2\n" p INPUT2 # > "3\nG 0\nS 3\nE -2\n" p OUTPUT2 # > "S 3\nG 0\nE -2\n" p INPUT3 # > "5\nA -2\nE 0\nW -5\nA -1\nE -20\n" p OUTPUT3 # > "A -3\nW -5\nE -20\n"
続いて問題を解くメソッド( solve
メソッドとします)を変数の下に定義します。
まず、入力データを受け取る処理を書きます。
def solve(input_lines) # 入力データ受け取り _, *lines = input_lines.split("\n") # 確認用コード lines end p solve(INPUT1) # > ["A 1", "D 6", "C 2", "G 4", "B 70", "A 10", "B 5"] p solve(INPUT2) # > ["G 0", "S 3", "E -2"] p solve(INPUT3) # > ["A -2", "E 0", "W -5", "A -1", "E -20"]
データが正しく受け取れていれば、solve
メソッドに文字と数字のペアを集計して数字で降順ソートする処理を追加していきます。
- 文字と整数のペアを配列に格納する(二次元配列になる)
- 既出の文字ならペアの整数を足し合わせる
- ペアの整数で降順に並び替える
def solve(input_lines) # 入力データ受け取り _, *lines = input_lines.split("\n") # 文字と数字のペアを集計する ary = [] lines.each do |line| s, d = line.split idx = ary.index { |x| x[0] == s } if idx ary[idx][1] += d.to_i else ary.push([s, d.to_i]) end end # 数字で降順ソートする sorted_ary = ary.sort_by { |x| x[1] }.reverse # 確認用コード sorted_ary end p solve(INPUT1) # > [["B", 75], ["A", 11], ["D", 6], ["G", 4], ["C", 2]] p solve(INPUT1) == OUTPUT1 # > false p solve(INPUT2) # > [["S", 3], ["G", 0], ["E", -2]] p solve(INPUT2) == OUTPUT2 # > false p solve(INPUT3) # > [["A", -3], ["W", -5], ["E", -20]] p solve(INPUT3) == OUTPUT3 # > false
あとは出力形式を整えれば完成です。
p solve(INPUT1) == OUTPUT1 -> true
を確認するために、文字と整数を半角スペースで連結した要素を改行区切りの文字列に変換して、末尾にも改行を加えます。
def solve(input_lines) # 入力データ受け取り _, *lines = input_lines.split("\n") # 文字と数字のペアを集計する ary = [] lines.each do |line| s, d = line.split idx = ary.index { |x| x[0] == s } if idx ary[idx][1] += d.to_i else ary.push([s, d.to_i]) end end # 数字で降順ソートする sorted_ary = ary.sort_by { |x| x[1] }.reverse # 文字と整数を半角スペースで連結した要素を改行区切りの文字列に変換して末尾に改行を加える sorted_ary.map { |item| item.join(" ") }.join("\n") << "\n" end p solve(INPUT1) # > "B 75\nA 11\nD 6\nG 4\nC 2\n" p solve(INPUT1) == OUTPUT1 # > true p solve(INPUT2) # > "S 3\nG 0\nE -2\n" p solve(INPUT2) == OUTPUT2 # > true p solve(INPUT3) # > "A -3\nW -5\nE -20\n" p solve(INPUT3) == OUTPUT3 # > true
確認用コードを標準入力からのデータ受け取りに変更して、動作確認をしたら提出します。
複数行のデータ受け取りなので STDIN.read
を使います。(入力終了は Ctrl+D
又は Ctrl+Z
)
解答コード
def solve(input_lines) # 入力データ受け取り _, *lines = input_lines.split("\n") # 文字と数字のペアを集計する ary = [] lines.each do |line| s, d = line.split idx = ary.index { |x| x[0] == s } if idx ary[idx][1] += d.to_i else ary.push([s, d.to_i]) end end # 数字で降順ソートする sorted_ary = ary.sort_by { |x| x[1] }.reverse # 文字と整数を半角スペースで連結した要素を # 改行区切りの文字列に変換して末尾に改行を加える sorted_ary.map { |item| item.join(" ") }.join("\n") << "\n" end puts solve(STDIN.read)
他の解答例
- 文字と整数のペアをハッシュで集計し、
sort_byメソッド
とreverseメソッド
で値の降順に並び替える
def solve(input_lines) # 入力データ受け取り _, *lines = input_lines.split("\n") # 文字と数字のペアを集計する hash = {} lines.each do |line| s, d = line.split if hash[s] hash[s] += d.to_i else hash[s] = d.to_i end end # 数字で降順ソートする sorted_hash = hash.sort_by { |k, v| v }.reverse.to_h # 文字と整数を半角スペースで連結した要素を # 改行区切りの文字列に変換して末尾に改行を加える sorted_hash.map { |item| item.join(" ") }.join("\n") << "\n" end puts solve(STDIN.read)
今回のまとめ
- 文字と整数のペアを、二次元配列(又はハッシュ)を使って集計を行いました
sort_byメソッド
とreverseメソッド
で使って二次元配列(又はハッシュ)の指定された要素の降順ソートを行いました- 二次元配列(又はハッシュ)の文字と整数のペアを半角スペースで連結しました
二次元配列やハッシュの指定要素での昇順・降順ソートは良く使うので慣れておきましょう!