알고리즘/프로그래머스

[프로그래머스] #.12901 - 2016년

2016년

문제 설명

2016년 1월 1일은 금요일입니다. 2016년 a월 b일은 무슨 요일일까요? 두 수 a ,b를 입력받아 2016년 a월 b일이 무슨 요일인지 리턴하는 함수, solution을 완성하세요. 요일의 이름은 일요일부터 토요일까지 각각 

SUN,MON,TUE,WED,THU,FRI,SAT

입니다. 예를 들어 a=5, b=24라면 5월 24일은 화요일이므로 문자열 TUE를 반환하세요.

제한 조건

  • 2016년은 윤년입니다.
  • 2016년 a월 b일은 실제로 있는 날입니다. (13월 26일이나 2월 45일같은 날짜는 주어지지 않습니다)

입출력 예

a b result
5 24 "TUE"

문제 해결하기

이것은 본래 달력을 만들 수 있는 알고리즘을 사용하게 되면 만들 수 있으나, 일반적으로 대부분의 언어에서는 time 패키지를 지원하기 때문에 이것을 사용하여 만들기로 했다. 이것의 다른 버전은 바로 아래에 이야기 할 것이다.

func solution(a int, b int) string

time 패키지에 있는 Date() 함수를 사용하여 년/일/월 등 시간 정보를 제공하면 영어로 된 문자열을 반환받을 수 있고 여기서 일부 글자만 잘라내어 슬라이스로 반환하도록 하자.

func solution(a int, b int) string {
	t := time.Date(2016, time.Month(a), b, 0, 0, 0, 0, time.UTC)
	return strings.ToUpper(t.Weekday().String()[:3])
}

func TestSolution(t *testing.T) {
	cases := []struct {
		month  int
		day    int
		expect string
	}{
		{5, 24, "TUE"},
	}
	for _, c := range cases {
		if r := solution(c.month, c.day); r != c.expect {
			t.Errorf("Month %#v, Day %#v; got %#v, want %#v", c.month, c.day, r, c.expect)
		}
	}
}

다른 방법으로 해결하기

time 패키지에 있는 함수를 사용하는 대신, 직접 기능을 구현해보는 것도 해볼만하다. 물론 패키지를 사용한 구현도 중요하긴 하다만, 이미 구현된 코드를 통해 알고리즘 문제를 푸는 것은 다소 본질과는 거리가 멀어지기 때문이다.

요일을 지정하고, 달 마다 며칠까지 있는지 파악하기

요일을 가지고 있는 목록과 달 마다 날짜가 최대 며칠까지 있는지 파악하는 것은 상당히 중요하다. 특히 문제에도 나와있듯이 2016년은 윤년이라고 하니, 2월은 28일이 아닌 29일까지가 된다.

var weekday = []string{
	"SUN",
	"MON",
	"TUE",
	"WED",
	"THU",
	"FRI",
	"SAT",
}

var DayOfMonth = []int{
	31, // 1월
	29, // 2월, 2016년은 윤년이다.
	31, // 3월
	30, // 4월
	31, // 5월
	30, // 6월
	31, // 7월
	31, // 8월
	30, // 9월
	31, // 10월
	30, // 11월
	31, // 12월
}

그런 다음, solution() 함수의 다른 버전을 작성해주면 되는데, 코드를 먼저 보도록 하자.

func solution(a int, b int) string {
	var d, w int

	for i := 0; i < a-1; i++ {
		d += DayOfMonth[i]
	}
	// 현재 주어진 날짜 이전까지 더한다.
	d += b-1
	// 5의 의미는 weekday 슬라이스에서 'FRI' 의 값이 위치한다.
	// 2016년 1월 1일은 금요일이기 때문에 더해야 한다.
	w = d%7 + 5
	// w 는 weekday 의 인덱스를 의미하기도 하기 때문에 넘어가지 않도록 한다.
	if w >= len(weekday) {
		w -= 7
	}

	return weekday[w]
}

생각보다 간단하다. 에를 들어 2016년 1월 31일의 요일을 구한다고 했을 때, 먼저 주어진 날짜까지 총 며칠인지 구할 필요가 있는데, 반복문은 그러한 것을 의미하고, 그 다음으로는 이번 달의 남은 날짜를 더해준다. 총 날짜에 7을 나눈 나머지는 날짜를 일주일 단위로 나누되 나누어 떨어지지 않는 것, 남아있는 일수를 의미한다.

 

1월의 날짜를 구한다면 반복문을 실행되지 않고, 31d 에 들어갈 것이고, 이러한 d7로 나눈 나머지는 3이 될 것이고 몫은 4가 될 것이다. 즉, 31일 까지는 4주 하고도 3일이 더 있는 것이 된다. 자 이제 여기서 요일을 구해야 하는데, 여기까지는 1월 1일이 일요일이라는 것을 가정했을 때의 이야기다. 하지만 2016년 1월 1일은 금요일이다. 위에서 정의한 weekday 을 참고하여 금요일에 맞추기 위해 금요일의 인덱스에 해당하는 5를 더한다. 

더 읽을거리

https://github.com/pronist/al.go/tree/main/programmers/_12901

https://programmers.co.kr/learn/courses/30/lessons/12901