스터디(1)

KIMYEONGJUN·2024년 2월 1일
0
post-thumbnail

목적

이직 스터디를 시작하면서 계속 프로그래머스 문제를 풀고있는데 조금이나마 코딩테스트에 익숙해지고싶어서 공부할겸 적어볼려고한다.

문제1

개인정보 수집 유효기간

문제 설명
고객의 약관 동의를 얻어서 수집된 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이 붙습니다.
파기해야 할 개인정보가 하나 이상 존재하는 입력만 주어집니다.

오늘 날짜는 2020년 1월 1일입니다.

첫 번째 개인정보는 D약관에 의해 2019년 5월 28일까지 보관 가능하며, 유효기간이 지났으므로 파기해야 할 개인정보입니다.
두 번째 개인정보는 Z약관에 의해 2020년 2월 14일까지 보관 가능하며, 유효기간이 지나지 않았으므로 아직 보관 가능합니다.
세 번째 개인정보는 D약관에 의해 2020년 1월 1일까지 보관 가능하며, 유효기간이 지나지 않았으므로 아직 보관 가능합니다.
네 번째 개인정보는 D약관에 의해 2019년 11월 28일까지 보관 가능하며, 유효기간이 지났으므로 파기해야 할 개인정보입니다.
다섯 번째 개인정보는 Z약관에 의해 2019년 3월 27일까지 보관 가능하며, 유효기간이 지났으므로 파기해야 할 개인정보입니다.

today는 오늘날짜 의미하는 문자열
terms는 약관의 유효기간을 담은 1차원 문자열 배열
privacies는 수집된 개인정보의 정보를 담은 1차원 문자열배열
1달은 28일

  1. 개인정보 + 약관
  2. 약관, 유효기간
  3. 오늘날짜

이런식으로 코드를 작성했다.

2020.01.01 이있으면
year 20(년) 12(달) 28(일)
month 1(달) * 28(일)
day 1
나눠주고 2020앞에 20을 제거해준다.

year + month + day 를하면 6749가 나온다.
today = 6749가 나온다.

termes는
Z = 3 28(일) = 84
D = 5
28(일) = 140

폐기는 today >= 2019.01.01 + D

import java.util.*;

class Solution {
    public int[] solution(String today, String[] terms, String[] privacies) {
        int[] answer = {};
        
        // today: 오늘 날짜
        int todayIntValue = dateToInt(today);
        
        // terms: 유효기간
        Map<String, Integer> termsStorage = new HashMap<>(); // 각 약관 정보를 저장할 맵 key 알파벳 value 숫자
        for(String term: terms) { // term 배열 순회
            String[] termInfo = term.split(" "); // ex) ["A 6"] A 6중간에 공백
            termsStorage.put(termInfo[0], Integer.parseInt(termInfo[1]) * 28); // 1달은 28일 key: 알파벳 value: 기간 * 28 
        }
        
        // privacies: 개인정보
        ArrayList<Integer> destroyList = new ArrayList<>(); // 파기할 개인정보의 순번을 저장할 리스트
        for(int i = 0; i <  privacies.length; i++) { // 오름차순으로 넣어줌 for문이 돌면서 privacies 배열 순회
            String[] privacyInfo = privacies[i].split(" "); // privacyInfo: 개인정보 분리
            // 수집일, 약관 종류
            int privacyCollectionDay = dateToInt(privacyInfo[0]);
            int termPeriod = termsStorage.get(privacyInfo[1]);
            if(privacyCollectionDay + termPeriod <= todayIntValue) { // 비교 기간 초과시 파기 대상으로 추가
                destroyList.add(i+1); // i + 1 privacies[i]는 i + 1 개인정보 수집일자 약관
            }
        }
        answer = new int[destroyList.size()]; // destroyList 크기만큼 정답 배열 크기 정의
        for(int i = 0; i < destroyList.size(); i++) { // destroyList의 원소를 정답 배열에 추가
            answer[i] = destroyList.get(i);
        }
        
        return answer;
    }
    
    // 함수 문자열 형식의 날짜를 정수형 날짜로 변환하는 메소드
    public int dateToInt(String date) {
        String dateInfo[] = date.split("[.]"); // .을 기준으로 날짜 문자열 분히
        // 년, 월, 일 정보 추출
        int year = Integer.parseInt(dateInfo[0].substring(2,4)); // 끝에자리 두개만 
        int month = Integer.parseInt(dateInfo[1]);
        int day = Integer.parseInt(dateInfo[2]);
        
        // 총 날짜 수 = 년 * 12 * 28 + 월 * 28 + 일
        return year * 12 * 28 + month * 28 + day;
    }
}

문제2

공원 산책

문제 설명
지나다니는 길을 'O', 장애물을 'X'로 나타낸 직사각형 격자 모양의 공원에서 로봇 강아지가 산책을 하려합니다. 산책은 로봇 강아지에 미리 입력된 명령에 따라 진행하며, 명령은 다음과 같은 형식으로 주어집니다.

["방향 거리", "방향 거리" … ]
예를 들어 "E 5"는 로봇 강아지가 현재 위치에서 동쪽으로 5칸 이동했다는 의미입니다. 로봇 강아지는 명령을 수행하기 전에 다음 두 가지를 먼저 확인합니다.

주어진 방향으로 이동할 때 공원을 벗어나는지 확인합니다.
주어진 방향으로 이동 중 장애물을 만나는지 확인합니다.
위 두 가지중 어느 하나라도 해당된다면, 로봇 강아지는 해당 명령을 무시하고 다음 명령을 수행합니다.
공원의 가로 길이가 W, 세로 길이가 H라고 할 때, 공원의 좌측 상단의 좌표는 (0, 0), 우측 하단의 좌표는 (H - 1, W - 1) 입니다.

공원을 나타내는 문자열 배열 park, 로봇 강아지가 수행할 명령이 담긴 문자열 배열 routes가 매개변수로 주어질 때, 로봇 강아지가 모든 명령을 수행 후 놓인 위치를 [세로 방향 좌표, 가로 방향 좌표] 순으로 배열에 담아 return 하도록 solution 함수를 완성해주세요.

제한사항
3 ≤ park의 길이 ≤ 50
3 ≤ park[i]의 길이 ≤ 50
park[i]는 다음 문자들로 이루어져 있으며 시작지점은 하나만 주어집니다.
S : 시작 지점
O : 이동 가능한 통로
X : 장애물
park는 직사각형 모양입니다.
1 ≤ routes의 길이 ≤ 50
routes의 각 원소는 로봇 강아지가 수행할 명령어를 나타냅니다.
로봇 강아지는 routes의 첫 번째 원소부터 순서대로 명령을 수행합니다.
routes의 원소는 "op n"과 같은 구조로 이루어져 있으며, op는 이동할 방향, n은 이동할 칸의 수를 의미합니다.
op는 다음 네 가지중 하나로 이루어져 있습니다.
N : 북쪽으로 주어진 칸만큼 이동합니다.
S : 남쪽으로 주어진 칸만큼 이동합니다.
W : 서쪽으로 주어진 칸만큼 이동합니다.
E : 동쪽으로 주어진 칸만큼 이동합니다.
1 ≤ n ≤ 9

입출력 예 설명
입출력 예 #1

입력된 명령대로 동쪽으로 2칸, 남쪽으로 2칸, 서쪽으로 1칸 이동하면 [0,0] -> [0,2] -> [2,2] -> [2,1]이 됩니다.

입출력 예 #2

입력된 명령대로라면 동쪽으로 2칸, 남쪽으로 2칸, 서쪽으로 1칸 이동해야하지만 남쪽으로 2칸 이동할 때 장애물이 있는 칸을 지나기 때문에 해당 명령을 제외한 명령들만 따릅니다. 결과적으로는 [0,0] -> [0,2] -> [0,1]이 됩니다.

입출력 예 #3

처음 입력된 명령은 공원을 나가게 되고 두 번째로 입력된 명령 또한 장애물을 지나가게 되므로 두 입력은 제외한 세 번째 명령만 따르므로 결과는 다음과 같습니다. [0,1] -> [0,0]

import java.util.*;

class Solution {
    static int startX;  // 로봇 강아지의 시작 x좌표
    static int startY;  // 로봇 강아지의 시작 y좌표

    public int[] solution(String[] park, String[] routes) {
        int[] answer = new int[2]; // 로봇 강아지의 최종 위치를 저장할 배열 선언, 배열 크기는 2 (x,y)

        // 동서남북 이동할 좌표를 map에 삽입
        Map<String, int[]> map = new HashMap<>();
        map.put("N", new int[]{-1, 0}); // 북쪽 위치
        map.put("S", new int[]{1, 0}); // 남쪽 위치
        map.put("E", new int[]{0, 1}); // 동쪽 위치
        map.put("W", new int[]{0, -1}); // 서쪽 위치

        // 로봇 강아지의 시작 위치를 나타내는 x,y좌표 초기화
        startX = 0;
        startY = 0;

        // park을 2차원 배열로 만들며 시작 위치 탐색
        String[][] arr = new String[park.length][park[0].length()];  // park의 크기에 맞는 2차원 배열을 생성
        for(int i=0; i < park.length; i++) {  // park의 각 원소(문자열)에 대해서
            String[] temp = park[i].split("");  // 원소를 한 글자씩 나눠서 temp 배열에 저장
            for(int j=0; j < temp.length; j++) {  // temp 배열의 각 원소에 대해서
                arr[i][j] = temp[j];  // arr의 해당 위치에 원소를 저장
                if(temp[j].equals("S")) {  // 만약 원소가 'S'(시작 위치)라면
                    startX = i;  // 시작 x좌표를 현재의 i값으로 설정
                    startY = j;  // 시작 y좌표를 현재의 j값으로 설정
                }
            }       
        }

        // routes를 순회하며 move 메서드 호출
        for(int i = 0; i < routes.length; i++) {  // routes의 각 원소(문자열)에 대해서
            String[] temp = routes[i].split(" ");  // 원소를 공백을 기준으로 나눠서 temp 배열에 저장
            int[] movePoint = map.get(temp[0]);  // temp 배열의 첫 번째 원소(이동 방향)를 키로 가지는 값을 맵에서 가져와 movePoint에 저장
            move(arr, startX, startY, movePoint, Integer.parseInt(temp[1]));  // move 메서드를 호출하여 이동을 시도. 이동하려는 칸의 정보, 현재 위치, 이동 방향, 이동 거리를 인자로 전달
        }

        answer[0] = startX;  // 최종 x좌표를 결과 배열의 첫 번째 원소에 저장
        answer[1] = startY;  // 최종 y좌표를 결과 배열의 두 번째 원소에 저장
        return answer;  // 결과 배열을 반환
    }

    private void move(String[][] arr, int startX, int startY, int[] direction, int steps) {
        int tempX = startX;  // 현재 x좌표를 tempX에 저장
        int tempY = startY;  // 현재 y좌표를 tempY에 저장
        int error = 0;  // error를 0으로 초기화. 이동 중에 장애물을 만나거나 범위를 벗어나면 1로 설정됩니다.
        for (int i = 0; i < steps; i++) {  // 주어진 거리만큼 이동을 시도
            tempX += direction[0];  // 이동 방향에 따른 x좌표의 변화를 tempX에 더함
            tempY += direction[1];  // 이동 방향에 따른 y좌표의 변화를 tempY에 더함
            // 만약 tempX, tempY가 범위를 벗어나거나, arr[tempX][tempY]가 'X'(장애물)이면
            if (tempX < 0 || tempX >= arr.length || tempY < 0 || tempY >= arr[0].length || arr[tempX][tempY].equals("X")) {
                error = 1;  // error를 1로 설정
                break;  // 더 이상의 이동을 중단
            }
        }
        if (error == 0) {  // 만약 error가 0이면, 즉 이동이 가능하면
            this.startX = tempX;  // 현재 x좌표를 tempX로 업데이트
            this.startY = tempY;  // 현재 y좌표를 tempY로 업데이트
        }
    }
}

처음에는 어떤 로직으로 돌아가는지 전혀이해가 안됐다. 너무 어려워서 다른사람의 코드를 참고를했지만 구현할때 어떻게 구연해야할지 몰라서 이것저것하다보니 끝내긴끝냈는데 문제가 너무 어려웠던것같다.

문제3

조건에 부합하는 중고거래 상태 조회하기(SQL)

문제 설명
다음은 중고거래 게시판 정보를 담은 USED_GOODS_BOARD 테이블입니다. USED_GOODS_BOARD 테이블은 다음과 같으며 BOARD_ID, WRITER_ID, TITLE, CONTENTS, PRICE, CREATED_DATE, STATUS, VIEWS은 게시글 ID, 작성자 ID, 게시글 제목, 게시글 내용, 가격, 작성일, 거래상태, 조회수를 의미합니다.

문제

USED_GOODS_BOARD 테이블에서 2022년 10월 5일에 등록된 중고거래 게시물의 게시글 ID, 작성자 ID, 게시글 제목, 가격, 거래상태를 조회하는 SQL문을 작성해주세요. 거래상태가 SALE 이면 판매중, RESERVED이면 예약중, DONE이면 거래완료 분류하여 출력해주시고, 결과는 게시글 ID를 기준으로 내림차순 정렬해주세요.

예시
USED_GOODS_BOARD 테이블이 다음과 같을 때

-- 코드를 입력하세요
SELECT BOARD_ID, WRITER_ID, TITLE, PRICE,
    CASE WHEN STATUS = 'SALE' THEN '판매중'
         WHEN STATUS = 'RESERVED' THEN '예약중'
         WHEN STATUS = 'DONE' THEN '거래완료'
    END AS STATUS
FROM USED_GOODS_BOARD
WHERE CREATED_DATE = '2022-10-05'
ORDER BY BOARD_ID DESC;
CASE
    WHEN 조건1 THEN 결과1
    WHEN 조건2 THEN 결과2
    ...
    ELSE 결과
END AS 컬럼명

이번에 SQL문제는 저번에 문제를 풀어서 그런지 편하게 풀었다.
CASE WHEN 조건 THEN 결과 SQL에서 이렇게 조건을 주는걸 처음으로 알게된것같다.
이번 문제를 풀면서 아직도 멀었다는 생각이 들었다.

마무리

처음에 문제를 봤을때 정말 어떻게 풀어야할지 감이 안왔다. 머리속으로는 가능할것같은데 코드로 구현하는게 어려웠다. 아직 프로그래머스 문제를 많이 안풀어서봐서 그런것같다.

profile
Junior backend developer

0개의 댓글