こんにちは!じゃいごテックのあつしです。
今回はpaizaレベルアップ問題集から宝くじという問題を解説していきたいと思います。こちらは過去にスキルチェックで実際に出題されていた問題らしいです!
難易度は 1428±8 で、C級としては平均的な難易度だと思います。
宝くじ (paizaランク C 相当) を解いてみる
※ paiza レベルアップ問題集 スキルチェック過去問題セットより
問題
今年もパイザ宝くじの季節がやってきました。パイザ宝くじ券には 100000 以上 199999 以下の 6 桁の番号がついています。毎年1つ当選番号 (100000 以上 199999 以下)が発表され、当たりクジの番号が以下のように決まります。
1等:当選番号と一致する番号
前後賞:当選番号の ±1 の番号(当選番号が 100000 または 199999 の場合,前後賞は一つしかありません)
2等:当選番号と下 4 桁が一致する番号(1等に該当する番号を除く)
3等:当選番号と下 3 桁が一致する番号(1等および2等に該当する番号を除く)
たとえば、当選番号が 142358 の場合、当たりの番号は以下のようになります。
- 1等:142358
- 前後賞:142357 と 142359
- 2等:102358, 112358, 122358, …, 192358 (全 9 個)
- 3等:100358, 101358, 102358, …, 199358 (全 90 個)
あなたが購入した n 枚の宝くじ券の各番号が入力されるので、それぞれの番号について、何等に当選したかを出力するプログラムを書いて下さい。
入力される値
入力は以下のフォーマットで与えられます。
b
n
a_1
a_2
:
a_n
ここで、b は当選番号、n は購入した宝くじの数、a_1,…,a_n は購入した宝くじ券の番号をそれぞれ表します。
入力値最終行の末尾に改行が1つ入ります。
期待する出力
期待する出力は n 行から成ります。各 i 行目 (1 ≦ i ≦ n) には、a_i が何等に当たったかに応じて、以下の文字列を出力して下さい。
1等の場合: first
前後賞の場合: adjacent
2等の場合: second
3等の場合: third
それ以外(外れ)の場合: blank
最後は改行し、余計な文字、空行を含んではいけません。
条件
すべてのテストケースにおいて、入力される値は全て整数であり、以下の条件をみたします。
- 100,000 ≦ b ≦ 199,999
- 1 ≦ n ≦ 100
- 100,000 ≦ a_i ≦ 199,999 (1 ≦ i ≦ n)
入力例1
142358
3
195283
167358
142359
出力例1
blank
third
adjacent
攻略ポイント
ポイント
- 標準入力から複数行のデータを取得する
- 剰余を利用して整数型の下 n 桁を取り出す
- 繰り返し、条件分岐
参考記事
問題を解く流れ
入出力例をコピペしてヒアドキュメントで変数に代入し、データを確認します。きちんと受け取れていれば確認用コードは削除します。
INPUT1 = <<~"EOS" 142358 3 195283 167358 142359 EOS OUTPUT1 = <<~"EOS" blank third adjacent EOS # 確認用コード p INPUT1 p OUTPUT1 # > "142358\n3\n195283\n167358\n142359\n" # > "blank\nthird\nadjacent\n"
続いて問題を解くメソッド( solve
メソッドとします)を変数の下に定義します。
まず、入力データを受け取る処理を書きます。
入力された文字列を改行で区切り整数型に変換し、1行目は当選番号 wining_num
、2行目は使わないので _
に代入、3行目以降を配列 tickets
に代入します。
def solve(input_lines) # 改行区切りで分割した要素を整数型に変換 # 1行目を wining_num に代入 # 2行目を _ に代入(使わないので捨てる) # 3行目以降を tickets 配列に格納 wining_num, _, *tickets = input_lines.split("\n").map(&:to_i) # 確認用コード [wining_num, tickets] end p solve(INPUT1) # > [142358, [195283, 167358, 142359]]
1つ目の要素に当たり番号、2つ目の要素に購入チケットの配列がデータが正しく受け取れていれば、次に solve
メソッドに当たりの判定処理を追加していきます。
tickets
の先頭から次々と当たりを確認して結果を配列で返す処理を追記していきます。
整数型の下 n 桁は 数値 % 10 ** n で取り出すことが出来ます。 例: 123456 % 10 ** 4 -> 3456
def solve(input_lines) # 改行区切りで分割した要素を整数型に変換 # 1行目を wining_num に代入 # 2行目を _ に代入(使わないので捨てる) # 3行目以降を tickets 配列に格納 wining_num, _, *tickets = input_lines.split("\n").map(&:to_i) # tikets 先頭から要素を次々取り出し処理結果を配列で返す tickets.each.map do |ticket| # 当たり確認 if ticket == wining_num # 1等 当たり番号と一致 "fitst" elsif ticket == wining_num - 1 || ticket == wining_num + 1 # 前後賞 当たり番号のプラスマイナス1 "adjacent" elsif ticket % 10 ** 4 == wining_num % 10 ** 4 # 2等 下4桁が一致 "second" elsif ticket % 10 ** 3 == wining_num % 10 ** 3 # 3等 下3桁が一致 "third" else # ハズレ "blank" end end end p solve(INPUT1) # >["blank", "third", "adjacent"]
この時点で p solve(INPUT1)
を puts solve(STDIN.read)
に変更すれば正解回答となりますが、p solve(INPUT1) == OUTPUT1 -> true
を確認するために、ひと手間加えてみます。
当たり判定結果を一旦 result
へ代入し、result
の各要素を改行で連結して、末尾に改行を追加したものを返すように変更します。
def solve(input_lines) # 改行区切りで分割した要素を整数型に変換 # 1行目を wining_num に代入 # 2行目を _ に代入(使わないので捨てる) # 3行目以降を tickets 配列に格納 wining_num, _, *tickets = input_lines.split("\n").map(&:to_i) # tikets 先頭から要素を次々取り出し処理結果を配列で返し result へ代入 result = tickets.each.map do |ticket| # 当たり確認 if ticket == wining_num # 1等 当たり番号と一致 "first" elsif ticket == wining_num - 1 || ticket == wining_num + 1 # 前後賞 当たり番号のプラスマイナス1 "adjacent" elsif ticket % 10 ** 4 == wining_num % 10 ** 4 # 2等 下4桁が一致 "second" elsif ticket % 10 ** 3 == wining_num % 10 ** 3 # 3等 下3桁が一致 "third" else # ハズレ "blank" end end # 要素を改行で連結し末尾に改行を追加して返す result.join("\n") << "\n" end p solve(INPUT1) p solve(INPUT1) == OUTPUT1 puts solve(INPUT1) # > "blank\nthird\nadjacent\n" # > true # > blank # > third # > adjacent
確認用コードを削除して、標準入力からのデータ受け取りに変更して動作確認をしたら提出します。
複数行のデータ受け取りなので STDIN.read
を使います。(入力終了は Ctrl+D
又は Ctrl+Z
)
解答コード
def solve(input_lines) # 改行区切りで分割した要素を整数型に変換 # 1行目を wining_num に代入 # 2行目を _ に代入(使わないので捨てる) # 3行目以降を tickets 配列に格納 wining_num, _, *tickets = input_lines.split("\n").map(&:to_i) # tikets 先頭から要素を次々取り出し処理結果を result へ代入 result = tickets.each.map do |ticket| # 当たり確認 if ticket == wining_num # 1等 当たり番号と一致 "first" elsif ticket == wining_num - 1 || ticket == wining_num + 1 # 前後賞 当たり番号のプラスマイナス1 "adjacent" elsif ticket % 10 ** 4 == wining_num % 10 ** 4 # 2等 下4桁が一致 "second" elsif ticket % 10 ** 3 == wining_num % 10 ** 3 # 3等 下3桁が一致 "third" else # ハズレ "blank" end end # 要素を改行で連結し末尾に改行を追加して返す result.join("\n") << "\n" end puts solve(STDIN.read)
他の解答例
文字列型の末尾 n 文字(下 n 桁)は 文字列 [-n..] で取り出すことが出来ます。 例: "123456"[-4..] -> "3456"
def solve(input_lines) # 改行区切りで分割した要素を整数型に変換 # 1行目を wining_num に代入 # 2行目を _ に代入(使わないので捨てる) # 3行目以降を tickets 配列に格納 wining_num, _, *tickets = input_lines.split("\n") # tikets 先頭から要素を次々取り出し処理結果を result へ代入 result = tickets.each.map do |ticket| # 当たり確認 if ticket == wining_num # 1等 当たり番号と一致 "first" elsif ticket == (wining_num.to_i - 1).to_s || ticket == (wining_num.to_i + 1).to_s # 前後賞 当たり番号のプラスマイナス1 "adjacent" elsif ticket[-4..] == wining_num[-4..] # 2等 下4桁が一致 "second" elsif ticket[-3..] == wining_num[-3..] # 3等 下3桁が一致 "third" else # ハズレ "blank" end end # 要素を改行で連結し末尾に改行を追加して返す result.join("\n") << "\n" end puts solve(STDIN.read)
今回のまとめ
今回は下 n 桁の取り出し方と、複数の条件分岐( if ~ elsif ~ else
)がポイントでした。
整数・文字列どちらのパターンでも対応できると、問題によって楽な方を選択できますね!