Python/프로그래머스

[프로그래머스 lv.1] 개인정보 수집 유효기간 (건너뛴 문제 다시풀기)

묘걍 2023. 9. 29. 11:28

문제

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

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

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

다음은 오늘 날짜가 2022.05.19일 때의 예시입니다.

  • 첫 번째 개인정보는 A약관에 의해 2021년 11월 1일까지 보관 가능하며, 유효기간이 지났으므로 파기해야 할 개인정보입니다.
  • 두 번째 개인정보는 B약관에 의해 2022년 6월 28일까지 보관 가능하며, 유효기간이 지나지 않았으므로 아직 보관 가능합니다.
  • 세 번째 개인정보는 C약관에 의해 2022년 5월 18일까지 보관 가능하며, 유효기간이 지났으므로 파기해야 할 개인정보입니다.
  • 네 번째 개인정보는 C약관에 의해 2022년 5월 19일까지 보관 가능하며, 유효기간이 지나지 않았으므로 아직 보관 가능합니다.

따라서 파기해야 할 개인정보 번호는 [1, 3]입니다.

오늘 날짜를 의미하는 문자열 today, 약관의 유효기간을 담은 1차원 문자열 배열 terms와 수집된 개인정보의 정보를 담은 1차원 문자열 배열 privacies가 매개변수로 주어집니다. 이때 파기해야 할 개인정보의 번호를 오름차순으로 1차원 정수 배열에 담아 return 하도록 solution 함수를 완성해 주세요.


제한사항

  • today는 "YYYY.MM.DD" 형태로 오늘 날짜를 나타냅니다.
  • 1 ≤ terms의 길이 ≤ 20
    • terms의 원소는 "약관 종류 유효기간" 형태의 약관 종류와 유효기간을 공백 하나로 구분한 문자열입니다.
    • 약관 종류는 A~Z중 알파벳 대문자 하나이며, terms 배열에서 약관 종류는 중복되지 않습니다.
    • 유효기간은 개인정보를 보관할 수 있는 달 수를 나타내는 정수이며, 1 이상 100 이하입니다.
  • 1 ≤ privacies의 길이 ≤ 100
    • privacies[i]는 i+1번 개인정보의 수집 일자와 약관 종류를 나타냅니다.
    • privacies의 원소는 "날짜 약관 종류" 형태의 날짜와 약관 종류를 공백 하나로 구분한 문자열입니다.
    • 날짜는 "YYYY.MM.DD" 형태의 개인정보가 수집된 날짜를 나타내며, today 이전의 날짜만 주어집니다.
    • privacies의 약관 종류는 항상 terms에 나타난 약관 종류만 주어집니다.
  • today와 privacies에 등장하는 날짜의 YYYY는 연도, MM은 월, DD는 일을 나타내며 점(.) 하나로 구분되어 있습니다.
    • 2000 ≤ YYYY ≤ 2022
    • 1 ≤ MM ≤ 12
    • MM이 한 자릿수인 경우 앞에 0이 붙습니다.
    • 1 ≤ DD ≤ 28
    • DD가 한 자릿수인 경우 앞에 0이 붙습니다.
  • 파기해야 할 개인정보가 하나 이상 존재하는 입력만 주어집니다.

 

예전 풀이

생각

today를 .을 기준으로 나눈다. split('.') → 리스트 탄생

terms를 ' '을 기준으로 나눈다 . split(' ') → 이중 리스트 탄생

privacies를 .과 ' '을 기준으로 나눈다

 

end_day

privacies의 [i][1] 의 값이 [j][0]과 같은지 확인

terms에 있는 값 [j][1]을 privacies의 날짜 중 월에 더해준다. 만약 월이 12가 초과하면 년에 1을 추가해준다. 일에는 -1을 해준다. 만약 일이 01이면 28로 바꿔준다.

 

비교

today[0] > privacies[i][0]이고 today[1] > privacies[i][1]이고 today[2] > privacies[i][2]이면

answer.append(i)

 

코드

def solution(today, terms, privacies):
    answer = []
    today_lst = today.split('.')
    end_ = []
    end_year = 0
    end_month = 0
    end_day = 0
    for i in range(len(privacies)):
        priva_ = privacies[i].split(' ')
        priva_lst = str(priva_[0]).split('.')
        priva_lst.append(priva_[1])
        for j in range(len(terms)):
            terms_lst = terms[j].split(' ')
            if priva_lst[i][3] == terms_lst[j][0]:
                term = terms_lst[j][1]
                if int(priva_lst[i][0][1]) + term < 12:
                    end_year = int(priva_lst[i][0][0])
                    end_month = int(priva_lst[i][0][1]) + term
                else:
                    end_year = int(priva_lst[i][0][0]) + 1
                    end_month = int(priva_lst[i][0][1]) + (term-12)
                if priva_lst[i][0][2] == '1':
                    end_day = 28
                else:
                    end_day = int(priva_lst[i][0][2]) -1
            end_ = [end_year, end_month, end_day]
            
        if int(today_lst[0]) > end_[0]:
            if int(today_lst[1]) > end_[1]:
                if int(today_lst[2]) > end_[2]:
                    answer.append(i)
    return answer

- 분명히 같아서 if문을 타야하는데 if문 내부로 들어가지 않고 else로 넘어가버린다... 둘이 같은거 아니야?

- 이 문제들 해결되면 함수 나누기,, 너무 길다

다시 생각해보기

생각

terms를 딕셔너리로 만들면 어떨까

pricavies에 terms를 더해서 today보다 이전이면 answer에 append

그냥 바로바로 생각나는대로 고쳐서 코드에 주석으로 달았다

코드

def solution(today, terms, privacies):
    answer = []
    today_lst = today.split('.')
    end_ = []
    end_year = 0
    end_month = 0
    end_day = 0
    term_dict = {}
    for i in terms:
        # key가 유형, value가 기간인 딕셔너리 만들기
        type_ = i.split(' ')
        term_dict[type_[0]] = type_[1]
    # privacies의 요소 구분하기
    for i in range(len(privacies)):
        # privacies i번째 요소를 날짜와 유형으로 나누기 (for문 돌 때 마다 초기화)
        priva_ = privacies[i].split(' ')
        # 나뉜 날짜를 년 월 일로 나누고 리스트에 넣기
        priva_lst = str(priva_[0]).split('.')
        # 유형도 리스트에 넣기
        priva_lst.append(priva_[1])
        # 최종적으로 [0]:년, [1]:월, [2]:일, [3]:유형의 리스트
        if priva_lst[3] in term_dict:
            # 유형에 해당하는 개월수 가져오기
            until = int(term_dict[priva_lst[3]])
            # 시작 날짜에 개월수를 더해주는데
            end_month = int(priva_lst[1]) + until
            # 만약 12를 초과한다면 년도에 더해주기
            if end_month > 12:
                end_year = int(priva_lst[0]) + 1
                end_month = end_month - 12
            else:
                end_year = int(priva_lst[0])
            # 일
            if int(priva_lst[2]) == 1:
                # 만약 1일에 시작했으면 28일에 끝남
                end_day = 28
            else:
                # 나머지는 -1일에 끝남
                end_day = int(priva_lst[2]) - 1
            end_ = [end_year, end_month, end_day]
        # 유효기간 초과 여부
        for j in range(3):
            if int(today_lst[j]) > end_[j]:
                answer.append(i+1)
                break
            else:
                continue
            
    return answer

- 코드 실행은 다 통과했는데 채점에서 하나밖에 통과 못 함

- 유효기간이 100까지도 갈 수 있다는 조건을 고려하지 못함! 단순히 12 초과하면 년도에 1추가는 안됨, 12로 나눠서 몫을 년도에 추가해주고 나머지를 월로..

2차 시도

def solution(today, terms, privacies):
    answer = []
    today_lst = today.split('.')
    end_ = []
    end_year = 0
    end_month = 0
    end_day = 0
    term_dict = {}
    for i in terms:
        # key가 유형, value가 기간인 딕셔너리 만들기
        type_ = i.split(' ')
        term_dict[type_[0]] = type_[1]
    # privacies의 요소 구분하기
    for i in range(len(privacies)):
        # privacies i번째 요소를 날짜와 유형으로 나누기 (for문 돌 때 마다 초기화)
        priva_ = privacies[i].split(' ')
        # 나뉜 날짜를 년 월 일로 나누고 리스트에 넣기
        priva_lst = str(priva_[0]).split('.')
        # 유형도 리스트에 넣기
        priva_lst.append(priva_[1])
        # 최종적으로 [0]:년, [1]:월, [2]:일, [3]:유형의 리스트
        if priva_lst[3] in term_dict:
            # 유형에 해당하는 개월수 가져오기
            until = int(term_dict[priva_lst[3]])
            # 시작 날짜에 개월수를 더해주는데
            end_month = int(priva_lst[1]) + until
            # 만약 12를 초과한다면 년도에 더해주기
            if end_month > 12:
                end_year = int(priva_lst[0]) + (end_month // 12)
                end_month = end_month % 12
            else:
                end_year = int(priva_lst[0])
            # 일
            if int(priva_lst[2]) == 1:
                # 만약 1일에 시작했으면 28일에 끝남
                end_day = 28
            else:
                # 나머지는 -1일에 끝남
                end_day = int(priva_lst[2]) - 1
            end_ = [end_year, end_month, end_day]
        # 유효기간 초과 여부
        for j in range(3):
            if int(today_lst[j]) > end_[j]:
                answer.append(i+1)
                break
            else:
                continue
            
    return answer

다른 사람 풀이

def time_convert(t) :
    year, month, day = map(int, t.split('.'))
    return year * 12 * 28 + month * 28 + day

def solution(today, terms, privacies):
    term_dict = dict()
    today = time_convert(today)
    answer = []    
    
    for term in terms :
        name, period = term.split()
        term_dict[name] = int(period) * 28
    
    for idx, privacy in enumerate(privacies) :
        start, name = privacy.split()
        end = time_convert(start) + term_dict[name]
        if end <= today :
            answer.append(idx+1)    
    
    return answer

코드 스스로 해석하고 이해하기

- time_convert 함수 정의

- map함수를 활용해 입력된 t값을 .을 기준으로 나눠 int형태로 바꾸고 각각을 year, month, day에 저장

** map() **
- 주어진 함수를 iterable(에: 리스트, 튜플 등)의 각 요소에 적용하여 결과를 반환
- 결과는 map object, 이를 list나 tuple과 같은 다른 형태로 변환 가능
- 기본 형태: map(function, iterable, ...)
     - function: 각 요소에 적용될 함수
     - iterable: 함수가 적용될 iterable 객체 (여러개 가능)
- lambda와 함께 자주 사용된다.

- 반환은 year에 12*28을 곱한 것 + month에 28을 곱한 것 + day를 통해 0년0월0일을 기준으로 며칠이 지났는지

- solution 함수에서

- term_dict라는 빈 딕셔너리를 생성하고

** dict() **
- 딕셔너리 생성에 사용됨
- 키워드 인자를 사용하거나 (dict(one=1) 하면 {'one':1})
- 튜플의 리스트를 사용해 생성하거나 (dict([('one', 1), ...]) 하면 {'one':1, ...})
 - zip()을 활용하거나 (dict(zip(iterable,iterable))

- today로 입력된 문자열을 time_convert함수를 통해 지나온 일수로 바꿔 today에 저장한다

- answer라는 빈 문자열을 생성한다

- terms의 요소만큼 순회하면서

- term을 ' '을 기준으로 나눠 name과 peroid에 저장한다

- term_dict에 key에는 name, value에는 peroid*28(개월수 * 28 = 개월수 동안의 일수)를 저장한다.

- privacies를 순회하며 각 요소와 인덱스 값을 가져온다

** enumerate() **
- iterable 객체를 입력으로 받아, 각 항목과 그 항목의 인덱스를 포함하는 튜플을 반환하는 열거 객체(enumerator)를 반환한다
- 기본 형태: enumerate(iterable, start=0)
     - iterable: 열거하려는 객체
     - start: 인덱스의 시작 값, 기본 값은 0
- 주로 for루프와 함게 사용하여 항목과 해당 항목의 인덱스에 동시에 접근

- privacies를 ' '를 기준으로 나누고 각각 start와 name에 저장한다

- time_convert함수에 start변수를 넣어 일수를 구하고 term_dict딕셔너리에서 해당 유형에 해당하는 개월수를 가져와 end에 저장한다

- 만약 end일수가 today일수보다 작거나 같다면 이미 유효기간이 지난 것이므로 privacies의 현재 요소의 인덱스값 +1을 answer에 더해준다.

 

def solution(today, terms, privacies):
    answer = []
    terms_dic = {t[0]: int(t[2:]) * 28 for t in terms}  # 약관 코드를 key값으로, 유효기간을 value값으로 하는 dict

    today = list(map(int, today.split('.')))
    today = today[0] * 12 * 28 + today[1] * 28 + today[2]  # 오늘 날짜를 일 단위로 변환

    for idx in range(len(privacies)):
        day, code = privacies[idx].split(' ')  # 개인정보 수집일과 약관 코드를 분리
        day = list(map(int, day.split('.')))
        day = day[0] * 12 * 28 + day[1] * 28 + day[2]  # 개인정보 수집일을 일 단위로 변환
        if day + terms_dic[code] <= today:  # 개인정보 수집일 + 유효기간이 오늘 날짜보다 이전이면
            answer.append(idx + 1)

    return answer

코드 스스로 해석하고 이해하기

- terms_dic을 생성해 terms를 순회하며 각 요소 t에 대하여 0번째 인덱스(유형이름)을 key로, (1번째 인덱스는 ' '), 2번째 인덱스부터는 int형으로 변환한 뒤 28을 곱하여 일수를 계산하고 그것을 value로 설정한다

- 입력 받은 today를 .을 기준으로 나누고 int형으로 바꿔 list에 추가해준다.

- today의 0번 인덱스인 년도에 12와 28을 곱한 것,  1번 인덱스인 월에 28을 곱한 것, 그리고 2번 인덱스인 날짜를 모두 더해 일수를 구한다

- privacies의 길이만큼 순회하며 privacies의 idx번째 요소를 ' '를 기준으로 나눠 day와 code에 저장한다

- day는 다시 . 을 기준으로 나눠 int형으로 바꾸고 list형태로 day에 저장한다

- day의 년 월 일을 일 단위로 변환하여 일수를 구한다

- 만약 day에 유효기간 개월수를 더한 일수가 today 일수보다 작거나 같다면 answer에 현재 요소 인덱스 +1 값을 더해준다