본문 바로가기

알고리즘/문제 풀이

99클럽 코테 스터디 26일차 TIL + [프로그래머스] 개인정보 수집 유효기간 풀이

1. 문제 설명

문제 링크

(1) 오늘이 몇년, 몇월, 며칠인지 알려주고, 개인정보의 유형별로 정보 보관 기간을 알려준다.

(2) String 배열 형태로, 정보가 수집된 날짜, 개인정보의 유형이 주어질 때, 주어진 배열에서 오늘 파기될 정보가 무엇인지, 번호를 배열 형태로 반환하라.

2. 접근 방식

KEY WORD: 문자열 자르기

해당 문제의 입력은 다음과 같이 주어진다.

today terms privacies result
"2022.05.19" ["A 6", "B 12", "C 3"] ["2021.05.02 A", "2021.07.01 B", "2022.02.19 C", "2022.02.20 C"] [1, 3]
"2020.01.01" ["Z 3", "D 5"] ["2019.01.01 D", "2019.11.15 Z", "2019.08.02 D", "2019.07.01 D", "2018.12.28 Z"] [1, 4, 5]

여타 다른 문제들과 다르게, 해당 문제는 하나의 문자열에 여러가지 의미를 담고 있다. 또한 해당 의미가 "." 혹은 띄어쓰기로 구분되어 있기도 하다.
따라서 해당 문제는 문자열을 의미별로 적절히 잘라서 사용할 수 있는지를 묻고 있는 문제이다. 나는 이를 위해서 StringTokenizer를 썼다. StringTokenizer(값, delim, delim 포함여부 boolean)의 인자가 들어간다. delim이 문자열을 나누는 기준이 되며, 3번째 인자의 true, false 여부에 따라 delim이 포함될지 안될지가 결정이 된다.

3. 코드 분석

import java.io.*;
import java.util.*;

class Solution {
    // 1. map에 값 저장 
    // 2. 저장한 개인정보를 기준으로, 파기해야할 날짜 계산 
        // (1) 달 + 저장기간 
        // (2) 달 + 저장기간이 12개월을 초과하면, 년도 +1
        // (3) 일은 현재 일자 -1 (만약 1일이라면 28일로 변경)
    // 3. today랑 비교해서 현재 파기해야하는지 확인

    static ArrayList<Integer> ans = new ArrayList<>();

    static HashMap<String, Integer> map = new HashMap<>();
    static int nowYear  = 0;
    static int nowMonth = 0;
    static int nowDays  = 0;
    public int[] solution(String today, String[] terms, String[] privacies) {

        StringTokenizer dDay = new StringTokenizer(today, ".", false);
        nowYear     = Integer.parseInt(dDay.nextToken());
        nowMonth    = Integer.parseInt(dDay.nextToken());
        nowDays     = Integer.parseInt(dDay.nextToken());


        // map에 값 저장
        for(int i = 0; i < terms.length; i ++){
            StringTokenizer st = new StringTokenizer(terms[i], " ", false);
            map.put(st.nextToken(), Integer.parseInt(st.nextToken()));
        }
        // 만료일 계산
        for(int i = 0; i < privacies.length; i++){
            StringTokenizer outer = new StringTokenizer(privacies[i]);
            StringTokenizer inner = new StringTokenizer(outer.nextToken(), ".", false);
            String term = outer.nextToken();
            int expire = map.get(term);

            int year    = Integer.parseInt(inner.nextToken());
            int month   = Integer.parseInt(inner.nextToken());
            int days    = Integer.parseInt(inner.nextToken());

            month += expire;

            if(month > 12){
                if(month%12 == 0){
                    year += month/12 -1;
                    month = 12;
                }else {
                    year += month/12;
                    month = month%12;
                }
            }

            days -= 1; 
            if(days == 0){
                month --;
                days = 28;
            }

            System.out.printf("파기 날짜: %d.%d.%d\n", year, month, days);

            if(nowYear > year) {
                ans.add((i+1)); 
            }else if(nowYear == year && nowMonth > month){
                ans.add((i+1)); 
            }else if(nowYear == year && nowMonth == month && nowDays > days){
                ans.add((i+1)); 
            }
        }
        return ans.stream().mapToInt(i -> i).toArray();
    }
}

4. 성장 하기

 month += expire;

            if(month > 12){
                if(month%12 == 0){
                    year += month/12 -1;
                    month = 12;
                }else {
                    year += month/12;
                    month = month%12;
                }
            }

            days -= 1; 
            if(days == 0){
                month --;
                days = 28;
            }

            System.out.printf("파기 날짜: %d.%d.%d\n", year, month, days);

            if(nowYear > year) {
                ans.add((i+1)); 
            }else if(nowYear == year && nowMonth > month){
                ans.add((i+1)); 
            }else if(nowYear == year && nowMonth == month && nowDays > days){
                ans.add((i+1)); 
            }

이까지의 식을 보면, 굳이 년, 월, 일을 따로 계산하여 문제를 풀어서, 문제를 기존보다 더 어렵게 풀었다. 년과 월은 모두 제일 작은 단위인 일(day)로 치환할 수 있다. 또한 문제에서 모든 달은 28일로만 이루어졌다고 했기에, 해당 계산이 훨씬 쉽다. 따라서 year -> day: (year) x 12 x 28, month -> day: (month) x 28 로 나타낼 수 있을 것이다. 이것을 이용해 모두 day로 치환 후 문제를 풀게 되면 위와 같이 가독성이 떨어지는 if, else문의 향연을 피할 수 있다!

    public boolean isExpire(int year, int month, int day, int expire) {
        int input = year*12*28 + month*28 + day + expire*28; 
        int now = nowYear*12*28 + nowMonth*28 + nowDays; 

        return input <= now? true : false;
    }

이런식으로 치환해서 비교하는 함수를 만들면

            if(isExpire(Integer.parseInt(inner.nextToken()),
                        Integer.parseInt(inner.nextToken()),
                        Integer.parseInt(inner.nextToken()), 
                        map.get(outer.nextToken()))) ans.add((i+1));

input 값만 적절히 넣어주면 바로 개인정보 보관 기간의 만료 여부를 알 수 있다.