[프로그래머스] (C++) 개인정보 수집 유효기간 <2023 KAKAO BLIND RECRUITMENT>

winluck·2023년 7월 1일
0

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

여름방학이라 프로그래머스에서 실전 코테 연습을 하고 싶어 가장 먼저 보이는 Lv 1 문제를 풀었는데, 생각보다 고생을 많이 해서 사고의 흐름을 기록하고 싶어 통과한 코드를 리뷰하고자 한다.

문제


설명을 천천히 읽어보면 결국 오늘이라는 날짜와 개인정보 수집일자 + 유효기간의 결과 날짜를 비교하여 만약 후자가 전자보다 더 과거(같은 날도 포함한다. 4번을 보면 2월 20일일 때 5월 19일까지 보관 가능하다고 설명하였다. 여기서 뻘짓했다..)일 때를 찾아 출력하라는 의미이다. 설명은 간단했지만 C++은 split이 없기 때문에 자체적으로 구현하는 데 시간이 꽤 걸렸고, 함정이 존재하여 꽤나 고생했던 문제이다.

제한사항

유효기간의 길이가 1 이상 100 이하, 즉 최대 3자리인 것에 유의하자.

입출력


문제를 제대로 이해하고 입출력을 바라보았다면 무엇을 해야 하는지는 바로 알 수 있으나, 막상 구현하려면 시간이 꽤 걸렸다.

크게 3가지 구간으로 나눌 수 있다.

1) 주어진 today 문자열의 년, 월, 일을 추출하여 저장

C++은 split이 없어 자체적으로 s라는 문자열을 선언한 뒤, 이를 한 글자씩 더하면서 인덱스를 전진하다 '.'을 만나면 이에 대응하는 코드를 작성하였다. 이때 계산이 쉽도록 stoi 함수를 활용하여 추출한 년, 월, 일을 int로 변환하였다.

2) 주어진 privacies 문자열 배열의 한 문자열의 마지막 글자, 즉 알파벳을 term에 있는 문자열의 첫 글자인 지점을 찾아, 거기서 유효기간을 추출

여기서 첫 번째로 했던 시행착오는 유효기간이 항상 한 자리인줄 알고 string에 내장된 back()을 이용해 단 한 글자만 추출한 것이다. 이후 실수를 인지하고 substr을 활용해 최대 3자리인 유효기간을 추출하였다. 참고로 만약 유효기간이 두 자리라고 해도, substr의 인자가 3이라고 해도 마지막 글자까지만 추출한다. 위와 마찬가지로 유효기간을 정수로 변환해주었다.

3) privacies 문자열 배열을 하나씩 순회하며 현재 개인정보의 날짜를 추출하여 유효기간을 더한 뒤 현재 날짜와 동일하거나 그 전이면 answer에 삽입

현재 개인정보를 추출하는 로직은 today의 그것과 유사하다.

특정 날짜에 유효기간을 더하여 만료일을 계산한다. 이때 유효기간은 month로 주어지고, 이를 더했을 때 13 이상인 경우 year을 증가시키고 유효기간은 이를 12로 나눈 나머지로 갱신해야 한다.

그러나 자꾸 1번, 14번, 17번 케이스에서 오답이 발생하기에 곰곰히 테스트 케이스를 떠올리다가 함정이 있음을 깨달았다.

생각해보자. 2022년 12월에서 유효기간이 12월이라면?
첫번째 조건문만 존재할 경우 (12 + 12) / 12 = 2, 즉 year가 2가 증가하고,
(12 + 12) % 12 = 0, 즉 month가 0
이 된다.

따라서 이에 대처하는 추가 로직이 필요하다. 두 값을 더한 결과가 12의 배수라면, 즉 thispartint[1]이 0이라면 이를 12로 바꾸고 year를 하나 깎아주었다.

대망의 마무리 부분은 날짜를 비교하는 것을 예시 날짜를 떠올리며 조건문으로 구현함현 된다. 이때 날짜가 동일해도 유효기간 만료인 것을 인지하지 못해 또 뻘짓을 했다.

시행착오

  • 동일한 날짜도 정답에 포함해야 한다는 사실 누락
  • 유효기간 파싱 관련 제한사항을 제대로 확인하지 않음
  • 작성한 로직의 예외처리 필요성을 자각하지 못함

소스 코드

#include <string>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> solution(string today, vector<string> terms, vector<string> privacies) {
    vector<int> answer;
    int todaypartint[3];
    int cnt = 0;
    string s = "";

    for(int i=0; i<today.size(); i++){
        if(today[i] == '.'){
            todaypartint[cnt] = stoi(s); // 추출한 문자열 정수로 변환
            cnt++;
            s = "";
            continue;
        }
        s += today[i];
    }
    todaypartint[cnt] = stoi(s);
    cnt = 0;
    s = "";
    // todaypart[0]은 년도, [1]은 월, [2]는 일
    
    for(int a=1; a<=privacies.size(); a++){
        int limits = 0;
        for(int j=0; j<terms.size(); j++){
            if(terms[j].front() == privacies[a-1].back()){ // privacies 배열의 한 원소의 마지막 글자는 terms 배열의 한 원소의 첫글자와 동일
                string t = terms[j];
                for(int b=0; b<terms[j].size(); b++){
                    if(t[b] == ' '){ // ' ' 다음에 유효기간이 등장한다.
                        t = t.substr(b+1, 3); // 유효기간은 최대 3자리이므로 
                        break;
                    }
                }
                limits = stoi(t); // 추출한 유효기간 문자열 정수로 변환
                break;
            }
        } // 이 개인정보의 종류를 결정하고 유효기간을 추출
        
        int thispartint[3];
        string now = privacies[a-1];
        for(int k=0; k<now.size(); k++){
            if(now[k] == '.'){
                thispartint[cnt] = stoi(s);
                cnt++;
                s = "";
                continue;
            }
            if(now[k] == ' '){
                thispartint[cnt] = stoi(s);
                cnt++;
                s = "";
                break;
            }
            s += now[k];
        } // 이 개인정보의 연, 월, 일을 today와 유사하게 추출
        cnt = 0;
        s = "";

        // 이제 특정 개인정보와 오늘과의 날짜 차이를 검증한다.
        // 특정 개인정보 + limits가 오늘보다 작을 경우 이를 포함해야 한다.
        if(thispartint[1] + limits > 12){
            thispartint[0] += (thispartint[1] + limits) / 12;
            thispartint[1] = (thispartint[1] + limits) % 12;
            if(thispartint[1] == 0){ // 예외처리: 12월인 상태에서 limits가 12면 24가 되는데, 이 경우 2년이 증가하므로 하나 깎아준다.
                thispartint[0]--;
                thispartint[1] = 12;
            }
        } else {
            thispartint[1] += limits;
        }
                        
        bool flag = false;
        if(todaypartint[0] > thispartint[0]){ // 2023 > 2022
            flag = true;
        } else if(todaypartint[0] == thispartint[0]){ 
            if(todaypartint[1] > thispartint[1]){ // 2023.07 > 2023.06
                flag = true;
            } else if(todaypartint[1] == thispartint[1]){
                if(todaypartint[2] >= thispartint[2]){ // 2023.07.20 > 2023.07.18
                    flag = true;
                }
            }
        }
        
        if(flag) answer.push_back(a); // true면 유효기간 만료
    }
    return answer;
}

교훈

  • split의 부재로 인해 자칫 소홀해질 수 있는 문자열 문제에 대응하는 능력을 기르자.
  • 문제 설명, 제한사항, 입출력 모두 침착하게 살펴보자.
  • 프로그래머스 UI/UX에 빨리 익숙해지자.
profile
Discover Tomorrow

0개의 댓글