[프로그래머스] 개인정보 수집 유효기간

DYKO·2023년 1월 18일
0

코딩 테스트

목록 보기
3/3

🔗 프로그래머스 문제로 이동하기

[문제 설명]

고객의 약관 동의를 얻어서 수집된 1~n번으로 분류되는 개인정보 n개가 있습니다. 약관 종류는 여러 가지 있으며 각 약관마다 개인정보 보관 유효기간이 정해져 있습니다. 당신은 각 개인정보가 어떤 약관으로 수집됐는지 알고 있습니다. 수집된 개인정보는 유효기간 전까지만 보관 가능하며, 유효기간이 지났다면 반드시 파기해야 합니다.

예를 들어, A라는 약관의 유효기간이 12 달이고, 2021년 1월 5일에 수집된 개인정보가 A약관으로 수집되었다면 해당 개인정보는 2022년 1월 4일까지 보관 가능하며 2022년 1월 5일부터 파기해야 할 개인정보입니다.
당신은 오늘 날짜로 파기해야 할 개인정보 번호들을 구하려 합니다.

모든 달은 28일까지 있다고 가정합니다.

[제한 사항]

  • 날짜는 "YYYY.MM.DD" 형태
    • 2000 ≤ YYYY ≤ 2022
    • 1 ≤ MM ≤ 12
    • 1 ≤ DD ≤ 28
  • 1 ≤ terms의 길이 ≤ 20
  • 1 ≤ privacies의 길이 ≤ 100
  • 구분자는 공백

[제출 답안]

import java.util.*;

class Solution {
    public int[] solution(String today, String[] terms, String[] privacies) {

        HashMap<String, Integer> policyMap = new HashMap<>();
        for(String policyInfo : terms){
            String policyType = policyInfo.split(" ")[0];
            String policyTerm = policyInfo.split(" ")[1];

            policyMap.put(policyType, Integer.parseInt(policyTerm));
        }

        List<Integer> deleteTarget = new ArrayList<>();

        for(int personNum = 1; personNum < privacies.length + 1; personNum++){
            String personalPrivacy = privacies[personNum - 1];
            String acceptanceDate = personalPrivacy.split(" ")[0];
            String policyType = personalPrivacy.split(" ")[1];
            PersonalPrivacyInfo info = new PersonalPrivacyInfo(acceptanceDate, policyMap.get(policyType));

            if (info.isUnvalid(today)) {
                deleteTarget.add(personNum);
            }
        }

        return deleteTarget.stream().mapToInt(i -> i).toArray();
    }

    private class PersonalPrivacyInfo {
        private DateBy28Day acceptanceDate;
        private DateBy28Day validDate;

        public PersonalPrivacyInfo(String acceptanceDate, int policyTerm) {
            this.acceptanceDate = new DateBy28Day(acceptanceDate);

            DateBy28Day validDate = new DateBy28Day(this.acceptanceDate);
            validDate.addMonth(policyTerm);
            validDate.addDay(-1);
            this.validDate = validDate;
        }

        public boolean isUnvalid(String today){
            return this.validDate.compare(new DateBy28Day(today)) < 0;
        }
    }

    public class DateBy28Day {
        private final static int DAY_OF_MONTH = 28;
        private final static int MONTH_OF_YEAR = 12;
        private int year;
        private int month;
        private int day;

        public DateBy28Day(String dateStr) {
            String[] dateArr = dateStr.split("\\.");
            this.year = Integer.parseInt(dateArr[0]);
            this.month = Integer.parseInt(dateArr[1]);
            this.day = Integer.parseInt(dateArr[2]);
        }

        public DateBy28Day (DateBy28Day date){
            this.year = date.year;
            this.month = date.month;
            this.day = date.day;
        }

        public void addMonth(int addMonthValue){
            this.year += addMonthValue/MONTH_OF_YEAR;
            this.month += addMonthValue%MONTH_OF_YEAR;

            if(this.month < 1) {
                this.month += MONTH_OF_YEAR;
                this.year--;
            } else if (this.month > MONTH_OF_YEAR){
                this.month -= MONTH_OF_YEAR;
                this.year++;
            }
        }

        public void addDay(int addDayValue){
            this.addMonth(addDayValue/DAY_OF_MONTH);
            this.day += addDayValue%DAY_OF_MONTH;

            if(this.day < 1){
                this.day += DAY_OF_MONTH;
                this.month--;
            } else if (this.day > DAY_OF_MONTH){
                this.day -= DAY_OF_MONTH;
                this.month++;
            }
        }

        public int compare (DateBy28Day compareDate) {
            if(this.year == compareDate.year && this.month == compareDate.month && this.day == compareDate.day){
                return 0;
            } else if(this.year < compareDate.year) {
                return -1;
            } else if(this.year > compareDate.year) {
                return 1;
            } else {
                if(this.month < compareDate.month) {
                    return -1;
                } else if (this.month > compareDate.month) {
                    return 1;
                } else {
                    return this.day < compareDate.day ? -1 : 1;
                }
            }
        }
    }
}

고려한 점

  1. 객체지향적 코드
    모든 월의 일자가 28일이므로 Date나 Calendar 같은 라이브러리 활용이 어렵기 때문에 따로 사용할 DateBy28Day 클래스를 구현하여 addMonth(), addDay(), commpare()와 같은 기능을 구현했다. 근데 다른 분 풀이 보니 진짜 멍청한 답안이었구나를 깨달았다..!

  2. List를 활용한 결과값 수집
    길이가 정해지지 않았으므로 List로 수집하여 toArray 하는 방식으로 구현했다. 다른 분 풀이에 왜 .mapToInt(i -> i)를 하냐는 질문이 있었는데, List에 받은 자료형은 Wrapper 객체인 Integer이므로 Primitive 타입인 int의 배열로 만들기 위해 형변환해주는 과정이다.


뒤늦게 깨달은 점

다 풀고 나니 생각난 건데, 알고리즘 문제는 객체지향적인 문제보다 얼마나 효율적으로 자료구조를 활용하는가가 중점인데 방향을 잘못 잡은 것 같다는 생각을 했다.
또한 내가 주로 쓰던 버전은 java 7 버전이다 보니 stream 기능에 익숙하지 않아, 이 부분을 따로 심층적으로 공부가 필요하다고 느꼈다. stream 이 for-loop문 보다 느린 측면이 있다고 하는데, 그럼에도 stream을 사용해야 하는 케이스를 알아볼 필요성이 있다...ㅠ

추가로, 알고리즘 문제도 결국 유형을 구분하고 효율적인 알고리즘을 적합하게 사용하는 것이 목적인데 이렇게 무작정 풀지 말고 유형과 그에 따른 알고리즘 정리하는 것부터 시작하는게 나을 것 같다... 무작정 푸는게 재미는 있는데 실력이 느는가에 대한 부분은 의문이다.


[ 다른 사용자 답안 유형 ]

1. Date 계산을 간단하게 하는 유형

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class Solution {
    public int[] solution(String today, String[] terms, String[] privacies) {
        List<Integer> answer = new ArrayList<>();
        Map<String, Integer> termMap = new HashMap<>();
        int date = getDate(today);

        for (String s : terms) {
            String[] term = s.split(" ");

            termMap.put(term[0], Integer.parseInt(term[1]));
        }
        for (int i = 0; i < privacies.length; i++) {
            String[] privacy = privacies[i].split(" ");

            if (getDate(privacy[0]) + (termMap.get(privacy[1]) * 28) <= date) {
                answer.add(i + 1);
            }
        }
        return answer.stream().mapToInt(integer -> integer).toArray();
    }

    private int getDate(String today) {
        String[] date = today.split("\\.");
        int year = Integer.parseInt(date[0]);
        int month = Integer.parseInt(date[1]);
        int day = Integer.parseInt(date[2]);
        return (year * 12 * 28) + (month * 28) + day;
    }
}

getDate() 메소드가 핵심이다. 날짜 계산을 단순하게 int형으로 변환하여 primitive 타입 비교 하나로 끝내 버렸다. 이렇게 하면 간단한 걸 삽질하고 있었다... Date 객체의 메소드들도 이런 식으로 날짜를 숫자 형식으로 변환하여 처리하는 방식으로 구현되어 있을 것 같다.



개선한 코드

package programmers.codingtest.level1;

import java.util.*;

/*################################
 * 플랫폼: 프로그래머스
 * 레벨: Level 1
 * 문제유형: 2023 KAKAO BLIND RECRUITMENT
 * URL: https://school.programmers.co.kr/learn/courses/30/lessons/150370
 * 비고: 개선된 코드
 * ################################
 */
public class PrivacyPolicyCheckerR2 {
    private final static int DAY_OF_MONTH = 28;
    private final static int MONTH_OF_YEAR = 12;

    public int[] solution(String today, String[] terms, String[] privacies) {

        HashMap<String, Integer> policyMap = new HashMap<>();
        for(String policyInfo : terms){
            String policyType = policyInfo.split(" ")[0];
            String policyTerm = policyInfo.split(" ")[1];

            policyMap.put(policyType, Integer.parseInt(policyTerm));
        }

        List<Integer> deleteTarget = new ArrayList<>();

        for(int personNum = 1; personNum < privacies.length + 1; personNum++){
            String acceptanceDate = privacies[personNum - 1].split(" ")[0];
            String policyType = privacies[personNum - 1].split(" ")[1];

            if (getDaysByStrDate(today) >= getValidDate(acceptanceDate, policyMap.get(policyType))) {
                deleteTarget.add(personNum);
            }
        }

        return deleteTarget.stream().mapToInt(i -> i).toArray();
    }

    private int getDaysByStrDate(String date){
        String[] dateArr = date.split("\\.");
        return convertDateToDays(Integer.parseInt(dateArr[0]), Integer.parseInt(dateArr[1]), Integer.parseInt(dateArr[2]));
    }

    private int getValidDate(String acceptanceDate, int policyMonth) {
        return  getDaysByStrDate(acceptanceDate) + (policyMonth * DAY_OF_MONTH);
    }

    private int convertDateToDays(int year, int month, int day) {
        return (year * MONTH_OF_YEAR * DAY_OF_MONTH) + (month * DAY_OF_MONTH) + day;
    }
}

사담

다른 분들도 비슷한 풀이다. 확실히 정해진 유형이 있나보다. 근데 회사 동료분이 왜 java로 하냐고... python이 더 유리하다며 python으로 공부하라고 추천해주셨다. 확실히 가벼운 알고리즘 문제는 python이 유리하긴 하다. python을 따로 공부하기 귀찮을 뿐...ㅎ

profile
엔지니어가 되는 그 날 까지!

0개의 댓글