このページについて
このページは、paiza ラーニング内に開設されているコンテンツ「レベルアップ問題集」で取り扱われているプログラミング課題について、独自の見解を述べたものです。
見解については、paizaラーニングの規約に基づき、許可されている範囲でのみ公開していますが、その内容については paiza とは一切関係なく、また paiza の立場を反映したものではありませんのでご注意ください。
挑戦する課題
レベルアップ問題集の日付セットから「翌営業日 – その2 (paizaランク B 相当)」を取り上げます。
以下は、問題公開 Web ページからの引用です。
問題
西暦2019年M月D日d曜日の翌営業日の日付を表示してください。
営業日は、休業日ではない日です。
休業日とは、土曜日、日曜日、または、内閣府の定める国民の祝日・休日のことです。
ただし、上記の条件を満たさない日は、休業日ではありません。例えば、官公庁や一般企業が休みとなる1月2日, 1月3日, 12月31日は休業日ではないものとします。
参考: 内閣府 「国民の祝日」について
- 1月:1日, 14日
- 2月:11日
- 3月:21日
- 4月:29日, 30日
- 5月:1日, 2日, 3日, 4日, 5日, 6日
- 7月:15日
- 8月:11日, 12日
- 9月:16日,23日
- 10月:14日, 22日
- 11月:3日, 4日, 23日
入力される値
整数MとD、英文字列dが次のように、スペース区切りで1行で入力されます。
1 |
M D d |
期待する出力
以下のような形式で、答えを出力してください。
1 |
A月B日 |
- AとBは、整数です。
条件
すべてのテストケースにおいて、以下の条件をみたします。
- 1 ≦ M ≦ 12
- 1 ≦ D ≦ 31
- dは、「SUN, MON, TUE, WED, THU, FRI, SAT」の7つの英文字列のうち、1つが入力されます。
- ただし、M=12, D=31, d=TUEのデータは与えられません。(年越しを考慮する必要はありません)
- ただし、M=2, D=31, d=MON のように、この世に存在しない日付は入力されません。(2月は28日までです)
考え方
まず、基本となるデータは辞書、もしくは配列の形で準備しておく。
たとえば、国民の祝日・振替休日はこのような感じ。
1 |
let nh = [1: [1, 14], 2: [11], 3: [21], 4: [29, 30], 5: [1, 2, 3, 4, 5, 6], 7: [15], 8: [11, 12], 9: [16,23], 10: [14, 22], 11: [3, 4, 23]] |
これは、
[Int: [Int]] 型の辞書、つまり辞書の
value が配列になっている辞書だが、このようにしておくことで、ある月の祝日を計算するのが楽になる。
たとえば、
m を月数とするとき
1 2 3 |
if let i = nh[m] { // i は祝日の配列 } |
このようにすれば、ある月 m の祝日は [Int] の配列として求めることができる。あとは、配列のメソッドである contains を使うことによって、ある日 d が祝日・振替休日か否かを判断すれば良い。
次の営業日の計算は次のように行なった。ここで、現在の曜日を
w とする。
まずは、曜日に対応する翌営業日の辞書
wd を準備する。
次に、曜日ラベルを
wdd という形で用意しておく。
1 2 3 4 5 6 7 8 9 |
// 翌営業日の辞書 let wd = ["SUN": 1, "MON": 1, "TUE": 1, "WED": 1, "THU": 1, "FRI": 3, "SAT": 2] // 曜日ラベル let wdd = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"] // 次の営業日を得る // (現在の曜日 + 翌営業日の日付) % 7 -> 翌営業日の曜日 w = wdd[(wdd.firstIndex(of: w)! + wd[w]!) % 7] |
注意しなければいけないのは、5月のように連休が続く時の処理。ここでは、ある日が休日だった時に処理を行った場合には、再度その処理を行うように repeat-while で処理をしたが、もっと賢い例があるかもしれない。
月跨ぎの処理については、過去問と同じなのでそれを流用する。
解答例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
// 一行入力し、空白で区切り、文字列に変換する let sa = readLine()!.split(separator: " ").map{ String($0) } // 計算しやすいように再代入 var m = Int(sa[0])! var d = Int(sa[1])! var w = sa[2] // 国民の祝日 let nh = [1: [1, 14], 2: [11], 3: [21], 4: [29, 30], 5: [1, 2, 3, 4, 5, 6], 7: [15], 8: [11, 12], 9: [16,23], 10: [14, 22], 11: [3, 4, 23]] // 月末31日以外のデータ let d30 = [2: 28, 4: 30, 6: 30, 9: 30, 11: 30] // 曜日ラベル let wdd = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"] // 翌営業日の辞書 let wd = ["SUN": 1, "MON": 1, "TUE": 1, "WED": 1, "THU": 1, "FRI": 3, "SAT": 2] // 繰り返しフラグ var cont = true let debug = false // デバッグ用仮入力 // 例1 //3 4 MON // 出力例1 //3月5日 //m = 3 //d = 4 //w = "MON" //入力例2 //7 12 FRI //出力例2 //7月16日 //m = 7 //d = 12 //w = "FRI" //入力例3 //8 10 SAT //出力例3 //8月13日 //m = 8 //d = 10 //w = "SAT" //入力例4 //8 11 SUN //出力例4 //8月13日 // m = 8 // d = 11 // w = "SUN" //入力例5 //4 26 FRI //出力例5 //5月7日 //m = 4 //d = 26 //w = "FRI" if debug { print("0:", m, d, w, cont) } // 翌営業日の日付を足す d += wd[w]! // 次の営業日を得る // (現在の曜日 + 翌営業日の日付) % 7 -> 翌営業日の曜日 w = wdd[(wdd.firstIndex(of: w)! + wd[w]!) % 7] if debug { print("1:", m, d, w, cont) } repeat { if let i = nh[m] { // 国民の休日の日と振替休日がある月 if i.contains(d) { // 休日だった場合、次の営業日を得る d += wd[w]! w = wdd[(wdd.firstIndex(of: w)! + wd[w]!) % 7] // 次の営業日が「国民の祝日」かもしれないので、再計算させる cont = true } else { cont = false } } // 月末最終日の計算 let ld = d30[m] ?? 31 if debug { print("2: ", m, d, w, cont) } // 翌営業日が月末を超えていた場合 if d > ld { // 日付を 1 に戻して(加算した分を引く)、月数を増やす d -= ld m += 1 } } while cont print("\(m)月\(d)日") |