20231224_개인정보 수집 유효기간(프로그래머스)

2023. 12. 24. 20:33IT/TIL

오늘의 TIL은 3일정도(매일 1시간씩) 시간이 소요된 프로그래머스에 있는

 

개인정보 수집 유효기간이라는 문제에 대한 개인적인 정리이다.

 

https://school.programmers.co.kr/learn/courses/30/lessons/150370

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

문제에 대해서 간략하게 설명하면,

 

개인정보 n개가 있을 때, 약관마다 유효기간이 정해져있어서, 유효기간이 지난 정보를 파기해야된다.

그러한 상황에서 개인정보 수집 일자와 약관의 유효기간을 알려줬을 때,

파기해야될 개인정보를 찾는 것이 이 문제의 핵심이다.

(제한사항이 많은데 이는 문제 링크에서 확인하는 것이 좋을 것 같다)

 

이 문제를 해결하는데 가장 중요한 사실은 유효기간은 XX달의 형식으로 달로 주어진다는 것이다.

 

즉, 개인정보 수집일자에 XX달을 더한 뒤에 today로 주어지는 비교해야되는 날짜와 비교하면된다.

또, 수집일자에 달을 더해서 오늘과 같은 날짜인 경우에는 유효기간이 지난 것이므로 폐기해야된다.

이 두 가지 사항이 이 문제에서 핵심이라고 생각한다.

 

이후에 문제를 풀이를 시작하면,

더보기

dictionary를 이용하여 풀이하고자 했던,

실패한 풀이법.

internal class Program
{
    static void Main(string[] args)
    {
        //string today = "2022.05.19";
        //string[] terms = { "A 6", "B 12", "C 3" };
        //string[] privacies = { "2021.05.02 A", "2021.07.01 B", "2022.02.19 C", "2022.02.20 C" };

        // today는 오늘 날짜
        // terms는 약관의 유효기간을 담은 1차원 문자열 배열
        // "A 6"의 경우 A 약관은 6개월 동안 보관해야된다는 뜻
        // privacies는 수집된 개인 정보와 약관을 담은 1차원 배열

        // 1달은 28일, 1년은 12달

        // terms를 split로 나눠서 정보 받아오기

        // terms는 약관의 종류 알파벳과 달 수만 주어짐
        // privacies에서 년과 달을 가져온 후에
        // 그 값에 종류에 맞는 알파벳의 값을 더해서 today와 비교하면 된다.

        // 즉, privacies[0]의 경우, 2021.05와 A를 가져와서 A = 6이므로
        // 2021.05 + 06 = 2021.11이 되고, today는 2022.05.19이므로 만료되었다고 판정하면 된다.

        // 해결 전략
        // privacies를 가져와서 알파벳을 읽고, 알파벳에 맞는 달 수를 더해준다.
        // 그 후에 그 값과 today를 비교해서 today가 작거나 같으면 폐기, 크면 폐기하지 않는다.

        //foreach (string text in privacies)
        //{
        //    string[] parts = text.Split(' ');
        //    string firstPart = parts[0];
        //    string secondPart = parts[1];

        //    Console.WriteLine($"{firstPart}, {secondPart}");
        //}

        // 위의 방법을 사용해서 privacies의 정보를 dictionary로 만들고
        // terms의 데이터도 dictionary로 만들어서 저장한 뒤에
        // privacies의 정보를 가지고 달을 더한 뒤에 today와 비교하게 만들면 될 것으로 보인다.

        string today = "2020.01.01";
        string[] terms = { "Z 3", "D 5" };
        string[] privacies = { "2019.01.01 D", "2019.11.15 Z", "2019.08.02 D", "2019.07.01 D", "2018.12.28 Z" };

        int[] result = new int[privacies.Length];

        DateTime todaydate = DateTime.ParseExact(today, "yyyy.MM.dd", null);

        // terms의 데이터를 dictionary를 이용하여 저장

        Dictionary<string, int> termsDict = SplitTerms(terms);

        for (int i = 0; i < privacies.Length; i++)
        {
            char checkAlpha = privacies[i][11];

            if (termsDict.TryGetValue(checkAlpha.ToString(), out int value))
            {
                string oldMonthStr = privacies[i].Substring(5, 2);
                int oldMonth = int.Parse(oldMonthStr) + value;

                if (oldMonth > 12)
                {
                    int year = oldMonth / 12;
                    int month = oldMonth % 12;

                    int currentYear = int.Parse(privacies[i].Substring(0, 4));
                    int currentMonth = int.Parse(privacies[i].Substring(5, 2));

                    //currentYear += year;
                    currentMonth += value;

                    if (currentMonth > 12)
                    {
                        currentYear += currentMonth / 12;
                        currentMonth %= 12;
                    }

                    privacies[i] = $"{currentYear:D4}.{currentMonth:D2}{privacies[i].Substring(7)}";
                }
                else
                {
                    privacies[i] = privacies[i].Substring(0, 5) + oldMonth.ToString("D2") + privacies[i].Substring(7);
                }

                string dateString = privacies[i].Substring(0, 10);
                DateTime checkdate = DateTime.ParseExact(dateString, "yyyy.MM.dd", null);

                int comparisonResult = checkdate.CompareTo(todaydate);
                if (comparisonResult > 0)
                {
                    result[i] += i;
                }
            }
        }

        foreach (int i in  result)
        {
            Console.WriteLine(i);
        }

        result = result.Where(val => val != 0).ToArray();

        int[] answer = result;

        //foreach (int i in answer)
        //{
        //    Console.WriteLine(i);
        //}

    }

    /// <summary>
    /// terms를 알파벳과 숫자로 분리하여 dictionary로 저장하는 함수입니다.
    /// 알파벳을 입력하면 해당하는 숫자를 불러올 수 있습니다.
    /// </summary>
    /// <param name="terms"></param>
    /// <returns></returns>
    private static Dictionary<string, int> SplitTerms(string[] terms)
    {
        Dictionary<string, int> dataDict = new Dictionary<string, int>();

        foreach (string pair in terms)
        {
            string[] keyValue = pair.Trim().Split(' ');
            string key = keyValue[0];
            int value = int.Parse(keyValue[1]);
            dataDict[key] = value;
        }

        return dataDict;
    }
}

 

위의 풀이는 실패한 풀이법으로 dictionary를 이용하여 문제를 해결하려 했으나,

조금 복잡한 구조(XXXX.XX.XX)를 그대로 가져가는 바람에 너무 복잡해져서 다시 시도했다.

 

아래는 프로그래머스에 입력하여 통과한 코드이다.

더보기
using System;
using System.Collections.Generic;

public class Solution {
    public int[] solution(string today, string[] terms, string[] privacies) {
        int[] result = new int[privacies.Length];
        int[] answer = new int[privacies.Length];
        List<int> tmpList = new List<int>();

        string todayStr = today.Replace(".", "");
        int todaydate = int.Parse(todayStr);

        // terms의 데이터를 dictionary를 이용하여 저장

        Dictionary<string, int> termsDict = SplitTerms(terms);

        for (int i = 0; i < privacies.Length; i++)
        {
            string[] privacyData = privacies[i].Split(' ');
            string privacyDateStr = privacyData[0].Replace(".", "");

            int privacyDate = int.Parse(privacyDateStr);
            string termKey = privacyData[1];

            if (termsDict.ContainsKey(termKey))
            {
                int termValue = termsDict[termKey];
                int checkDate = 0;

                int year = privacyDate / 10000;
                int month = privacyDate % 10000;
                month = month + termValue * 100;

                if (month > 1300)
                {
                    int tmp = month / 1200;
                    year += tmp;
                    month -= tmp * 1200;
                    if(month / 100 == 0)
                    {
                        month += 1200;  
                        year--;
                    }
                }

                checkDate = year * 10000 + month;
                result[i] = checkDate - todaydate;

                if (result[i] <= 0)
                {
                    tmpList.Add(i + 1);
                }
            }
        }
        
        answer = tmpList.ToArray();        
        return answer;
    }
    
    private static Dictionary<string, int> SplitTerms(string[] terms)
    {
        Dictionary<string, int> dataDict = new Dictionary<string, int>();

        foreach (string pair in terms)
        {
            string[] keyValue = pair.Trim().Split(' ');
            string key = keyValue[0];
            int value = int.Parse(keyValue[1]);
            dataDict[key] = value;
        }

        return dataDict;
    }
}

 

 

기본적인 풀이 전략은 이전에 시도했던 풀이법과 같으나,

우선적으로 "."을 없애는 과정을 거친 후에 해당하는 string을 int로 바꾸는 과정을 거쳤다.

 

그 후에 데이터에서 year, month 를 나눠서 처리하기로 한 후에 for 안에 if 문을 사용하였다.

기본적으로 주어지는 privacies의 날짜를 int 값으로 바꾼 privacyDate를 사용하였는데,

 

year = privacyDate / 10000으로, month = privacyDate % 10000;으로 한 후에,

month에 termValue(약관에 따른 기간)을 더해주었다.

그 이후에 그 값이 1300보다 크면(1월 이후가 되면)

int 값 tmp를 도입하여 year에 해당하는 만큼 년 수를 추가하고,

그 만큼을 month로 빼주었다.

단, 12월의 경우에는 이 작업으로 문제가 생기므로

그 안에 if문 달의 자리에 아무 수도 없다면 (month / 100 == 0)

month에 12월을 추가하고, 1년을 빼는 식으로 보정해주었다.

 

이후에 여기서 구한 비교용 Date를 가지고 todaydate와 비교하였는데, // todayDate로 해야되는데 오타가 있었습니다

주의사항이었던 ( 또, 수집일자에 달을 더해서 오늘과 같은 날짜인 경우에는 유효기간이 지난 것이므로 폐기해야된다)를

살려서 if (result[i] <= 0)으로 만든 후에

List에 추가하고, 이후에 answer는 이 List를 배열로 변경해주는 것으로 마무리지었다.

 

 

체감상 거의 일주일을 투자했던 코딩테스트 문제인데,

첫 날에는 Dictionary를 공부한다고 그 개념을 공부하느랴 거의 대부분의 시간을 보냈다.

그래서 만들 수 있었던 Dictionary가 아래와 같은데, 내용을 살펴보면

우선 string[] KeyValue를 선언하고 pair.Trim().Split(' '); 를 사용해서 값을 넣어주는데,

pair.Trim()은 문자열의 앞뒤에 있는 공백을 제거하는 기능을 가지고 있으며,

Split(' ')을 통해 공백을 나누어서 KeyValue 값을 가져온다.

따라서 지금의 문제에서는 KeyValue[0]에는 알파벳이, KeyValue[1]에는 숫자가 들어가는 dictionary를 만들 수 있게 해준다.

이후에 알파벳을 key로 숫자를 value로 만들어주었다.

즉, 이 dictionary를 이용하면 특정한 알파벳을 입력했을 때,

그에 대응되는 유효기간의 개월 수를 알려주는 사전을 만들어낸 것이다.

private static Dictionary<string, int> SplitTerms(string[] terms)
{
    Dictionary<string, int> dataDict = new Dictionary<string, int>();

    foreach (string pair in terms)
    {
        string[] keyValue = pair.Trim().Split(' ');
        string key = keyValue[0];
        int value = int.Parse(keyValue[1]);
        dataDict[key] = value;
    }

    return dataDict;
}

 

 

이 문제를 풀면서 자주 사용하지 못했었던 dictionary에 대한 개념적 이해와 더불어 실제로 사용해볼 수 있는 기회를 가졌다.

 

이후에 문제를 푸는 과정을 개념적 오류와 시행착오를 겪었는데,

 

처음에는 "."을 그대로두고 year, month만 나누어서(day는 이 문제에서 마지막에 비교하는 과정에서만 쓰이므로)

가져온 후에 그 값을 가지고 유효기간을 더하여 새로운 비교용 날짜(checkDate)를 만들고

비교해야되는 날짜(today)와 비교하여 그 값과 같으면(어제까지가 유효기간이므로) 폐기하고,

그 값보다 작으면 유효기간이 지났으므로 폐기하고 값보다 크면 남기는 식으로 만들려고 했었다.

 

이 과정에서 "."이 들어간채로 작업을 진행하니 오류가 발생했는데

(이는 제 실력으로 인하여 생긴 오류로 생각됩니다)

오류의 내용은 달이 12월이 넘어가는 과정에서 년도에 +1 해주는 과정에서 자릿수에 문제가 생긴다거나,

달이 100월이 넘어가는 경우에 문제가 생긴다거나 하는 문제들이었다.

 

 

이러한 문제들을 해결하고자 다시금 도전했던 방법은

처음부터 "."을 없앤 데이터를 만든 후에, 그 값을 숫자로 만들어서 계산하는 것으로

위에 있는 코드가 그 결과물이다.

 

이 방법의 아이디어의 핵심은 첫 줄에 나타나는

 

string todayStr = today.Replace(".", "");으로 .을 없애는 과정이다.

보기에는 단순한 1줄의 코드이지만, 이 코드를 수행함으로써 이전에 겪었던 고생들(위의 오류들)을 해결할 수 있었다.

 

이후에 데이터들을 사용할 수 있게 작업해주고, 그 작업한 데이터들을 가지고 각각의 유효기간을 더하는 작업을 거쳤는데,

termKey = privacyData[1]로써 알파벳을 선정해주는 작업이나

if (termsDict.ContainsKey(termKey))의 작업은 dictionary를 사용하는 방법으로써

이를 터득한 것이 이번 문제의 수확이라고 할 수 있다.

 

이후에는 수학적 아이디어를 이용하여 작업을 진행했는데,

 

처음에는 유효기간이(terms의 수치값이) 작은 숫자로만 생각하고 코딩하여서 오류가 발생했었는데,

제한사항에 유효기간은 1 ~ 100으로 주어진 것이 문제의 원인이였다.

 

따라서 year와 month를 나누어서(10000으로 나눈 몫과 나머지로) 작업을 진행했으며,

month의 값이 1300이 넘으면 year에 올려주는 작업을 진행했다.

이 부분에서 month가 12월인 경우에도 그에 대한 작업을 해주는 것을 추가했다.

 

이로써 나온 checkDate를 가지고 todaydate(todayDate가 맞는데 오타입니다)

두 값을 비교하여 값이 0보다 같거나 작으면 폐기해야된다고 표기하도록 Add를 해주었다.

(이 과정에서 배열로 답을 제출해야되니 tmpList를 만들어서 Add하고

 이후에 배열로 바꾸어주는 과정을 거쳤다)

 

이러한 과정을 거쳐서 해당하는 문제를 해결할 수 있었다.

이 문제를 통해서 dictionary에 대한 이해도를 올릴 수 있는 좋은 기회가 되어서 좋았다고 생각한다.

또한, 문제를 풀이하는 과정에서 요일(년월일)에 대한 요소들의 특이성을 한 번 더 공부할 수 있는 기회가 되었다.

 

다른 풀이를 보면

처음에 year, month, day를 다 나눈 후에 특정한 숫자값으로 만드는 과정을 거치는 풀이도 있는데

day = day, month = month * 28(1달을 28일로 고정했으므로), year = year * 12 * 28로 만든 후에

이들을 다 더해서 숫자값 todaySum을 만들어 둔 후에,

각 privacies에 대해서 같은 과정을 거쳐서 값을 비교하는,

내가 생각했던 작업보다 훨씬 더 간단명료하게 작업을 진행하는 풀이법도 있었다.

 

 

이 문제는 문제를 풀면서 다양하게 생각할 수 있었던 좋은 문제였다고 생각한다.

'IT > TIL' 카테고리의 다른 글

20231227_IPointer Interface  (0) 2023.12.27
20231226_달리기 경주(프로그래머스)  (0) 2023.12.26
20231222_NavMesh  (0) 2023.12.23
20231221_팀 프로젝트 회고  (0) 2023.12.22
20231220_AudioSource  (1) 2023.12.21