📑 문1) 얀에서는 매년 달리기 경주가 열립니다. 해설진들은 선수들이 자기 바로 앞의 선수를 추월할 때 추월한 선수의 이름을 부릅니다. 예를 들어 1등부터 3등까지 "mumu", "soe", "poe" 선수들이 순서대로 달리고 있을 때, 해설진이 "soe"선수를 불렀다면 2등인 "soe" 선수가 1등인 "mumu" 선수를 추월했다는 것입니다. 즉 "soe" 선수가 1등, "mumu" 선수가 2등으로 바뀝니다.
선수들의 이름이 1등부터 현재 등수 순서대로 담긴 문자열 배열 players와 해설진이 부른 이름을 담은 문자열 배열 callings가 매개변수로 주어질 때, 경주가 끝났을 때 선수들의 이름을 1등부터 등수 순서대로 배열에 담아 return 하는 solution 함수를 완성해주세요.
제한사항
입출력 예
players | callings | result |
---|---|---|
["mumu", "soe", "poe", "kai", "mine"] | ["kai", "kai", "mine", "mine"] | ["mumu", "kai", "mine", "soe", "poe"] |
입출력 예 설명
4등인 "kai" 선수가 2번 추월하여 2등이 되고 앞서 3등, 2등인 "poe", "soe" 선수는 4등, 3등이 됩니다. 5등인 "mine" 선수가 2번 추월하여 4등, 3등인 "poe", "soe" 선수가 5등, 4등이 되고 경주가 끝납니다. 1등부터 배열에 담으면 ["mumu", "kai", "mine", "soe", "poe"]이 됩니다.
나의 풀이
배열로 풀기 ( 런타임 에러)
package programmers;
public class RunningRace {
public static String[] solution(String[] players, String[] callings) {
String temp = "";
for (String calling : callings) {
for (int i = 0; i < players.length; i++) {
if (players[i].equals(calling)) {
temp = players[i - 1];
players[i - 1] = players[i];
players[i] = temp;
break;
}
}
}
return players;
}
public static void main(String[] args) {
String[] players = {"mumu", "soe", "poe", "kai", "mine"};
String[] callings = {"kai", "kai", "mine", "mine"};
solution(players,callings);
}
}
Map으로 풀기
import java.util.*;
public class RunningRace {
public static String[] solution(String[] players, String[] callings) {
Map<String, Integer> playerIndexMap = new HashMap<>();
for (int i = 0; i < players.length; i++) {
playerIndexMap.put(players[i], i);
}
for (String calling : callings) {
int callingIndex = playerIndexMap.get(calling);
if (callingIndex > 0) {
String temp = players[callingIndex - 1];
players[callingIndex - 1] = players[callingIndex];
players[callingIndex] = temp;
playerIndexMap.put(players[callingIndex], callingIndex);
playerIndexMap.put(players[callingIndex - 1], callingIndex - 1);
}
}
return players;
}
public static void main(String[] args) {
String[] players = {"mumu", "soe", "poe", "kai", "mine"};
String[] callings = {"kai", "kai", "mine", "mine"};
String[] result = solution(players, callings);
System.out.println(Arrays.toString(result));
}
}
나의 생각
반복문으로 푼 경우
처음에는 새로운 배열을 만들지 않고, 매개변수로 주어지는 String[] players
에 값을 덮어 씌우면 되겠다는 생각을 하였다. 먼저 이중 for문으로 callings를 반복을 돌리고, players.length 만큼 반복을 돌려, players[i].equals(calling)
조건을 만족하면, 앞 선수와 자리를 교체해주는 방법을 사용하였다..
테스트 결과에서 알 수 있듯이, Lv.1 부터는 시간복잡도 까지 고려하여 문제를 풀어야 하는거 같은데, 이 문제는 최악의 경우에 시간 복잡도는 O(N×M)
이므로 제한 사항에 따르면 50,000,000,000 ( 50,000 * 1,000,000)
번에 해당하는 반복을 수행해야하기 때문에 알고리즘 문제에서는 엄청 긴 시간을 소요하는 샘이다.
Map 컬랙션 사용
반복문의 로직들을 Map 컬랙션을 사용하여 변환해주었다 . 등수를 생각했을 때 1등부터 시작하는게 맞지만, 편의상 0번 Index부터 차례로 배치하였다.
핵심 문법을 보자면, 반복문을 사용했을 때와 마찬가지로 temp 임시변수를 활용하여 자리 교체는 동일 하지만, 교체된 Key
, Value
값을 다시 Map 컬랙션에 넣어 실시간으로 순서를 업데이트 시켰다.
결론적으로, 두 코드는 같은 기능을 수행하지만, 두 번째 코드 블록이 좀 더 효율적인 방법으로 구현했다는 점이다. HashMap을 사용하여 요소와 인덱스 간의 매핑을 관리하고, 위치 변경 후에도 매핑을 업데이트하여 반복문 내에서 인덱스를 다시 찾는 과정을 피할 수 있다. 따라서, 두 번째 코드 블록의 실행 속도가 더 빠를 것이다.
📑 문2) 사진들을 보며 추억에 젖어 있던 루는 사진별로 추억 점수를 매길려고 합니다. 사진 속에 나오는 인물의 그리움 점수를 모두 합산한 값이 해당 사진의 추억 점수가 됩니다. 예를 들어 사진 속 인물의 이름이 ["may", "kein", "kain"]이고 각 인물의 그리움 점수가 [5점, 10점, 1점]일 때 해당 사진의 추억 점수는 16(5 + 10 + 1)점이 됩니다. 다른 사진 속 인물의 이름이 ["kali", "mari", "don", "tony"]이고 ["kali", "mari", "don"]의 그리움 점수가 각각 [11점, 1점, 55점]]이고, "tony"는 그리움 점수가 없을 때, 이 사진의 추억 점수는 3명의 그리움 점수를 합한 67(11 + 1 + 55)점입니다.
그리워하는 사람의 이름을 담은 문자열 배열 name, 각 사람별 그리움 점수를 담은 정수 배열 yearning, 각 사진에 찍힌 인물의 이름을 담은 이차원 문자열 배열 photo가 매개변수로 주어질 때, 사진들의 추억 점수를 photo에 주어진 순서대로 배열에 담아 return하는 solution 함수를 완성해주세요.
제한사항
입출력 예
name | yearning | photo | result |
---|---|---|---|
["may", "kein", "kain", "radi"] | [5, 10, 1, 3] | [["may", "kein", "kain", "radi"],["may", "kein", "brin", "deny"], ["kon", "kain", "may", "coni"]] | [19, 15, 6] |
["kali", "mari", "don"] | [11, 1, 55] | [["kali", "mari", "don"], ["pony", "tom", "teddy"], ["con", "mona", "don"]] | [67, 0, 55] |
["may", "kein", "kain", "radi"] | [5, 10, 1, 3] | [["may"],["kein", "deny", "may"], ["kon", "coni"]] | [5, 15, 0] |
입출력 예 설명
입출력 예 #1
입출력 예 #2
나의 풀이
import java.util.*;
class Solution {
public int[] solution(String[] name, int[] yearning, String[][] photo) {
Map<String, Integer> memory = new LinkedHashMap<>();
for(int i = 0; i < name.length; i++) {
memory.put(name[i],yearning[i]);
}
int temp = 0;
int[] answer = new int[photo.length];
for(int i = 0; i < photo.length; i++) {
for(int j = 0; j < photo[i].length; j++) {
for(Map.Entry<String, Integer> entry : memory.entrySet()) {
if(photo[i][j].equals(entry.getKey())) {
temp += entry.getValue();
}
}
}
answer[i] = temp;
temp = 0;
}
System.out.println(Arrays.toString(answer));
return answer;
}
}
나의 생각
이번 문제는 Map 컬랙션을 사용하면 쉽게 해결할 수 있는 문제였던거같다. 먼저, 사진 속 그리움의 대상인 NAME
과, 그리움의 대상의 점수를 yearning
이라고 할 때, Map의 key를 NAME
, value를 yearning
으로 접근하였다.
쉽게 말해, photo 2차원 배열에 든 원소의 총 합을 배열로 나타내자
가 핵심으로, 나는 for문 반복을 돌려, 해당 인덱스에 저장된 원소와, memory score(Map의 key
값)를 비교하여, 같은 name
이 있으면 임시 변수 temp에 합산 결과를 저장하여, 최종적으로 answer 배열에 점수를 추가 하는 방법을 사용하였다. 핵심 로직은 LV.0 알고리즘 문제를 풀 때에도, 수없이 사용했던
for(Map.Entry<String, Integer> entry : memory.entrySet()) {
if(photo[i][j].equals(entry.getKey())) {
temp += entry.getValue();
}
}
Map의 entry인터페이스를 활용하여, 해당하는 key와 value를 비교할 수 있는 아주 유용한 로직이다.
📑 문3) 지나다니는 길을 'O', 장애물을 'X'로 나타낸 직사각형 격자 모양의 공원에서 로봇 강아지가 산책을 하려합니다. 산책은 로봇 강아지에 미리 입력된 명령에 따라 진행하며, 명령은 다음과 같은 형식으로 주어집니다.
["방향 거리", "방향 거리" … ]
예를 들어 "E 5"는 로봇 강아지가 현재 위치에서 동쪽으로 5칸 이동했다는 의미입니다. 로봇 강아지는 명령을 수행하기 전에 다음 두 가지를 먼저 확인합니다.
주어진 방향으로 이동할 때 공원을 벗어나는지 확인합니다.
주어진 방향으로 이동 중 장애물을 만나는지 확인합니다.
위 두 가지중 어느 하나라도 해당된다면, 로봇 강아지는 해당 명령을 무시하고 다음 명령을 수행합니다.
공원의 가로 길이가 W, 세로 길이가 H라고 할 때, 공원의 좌측 상단의 좌표는 (0, 0), 우측 하단의 좌표는 (H - 1, W - 1) 입니다.
공원을 나타내는 문자열 배열 park, 로봇 강아지가 수행할 명령이 담긴 문자열 배열 routes가 매개변수로 주어질 때, 로봇 강아지가 모든 명령을 수행 후 놓인 위치를 [세로 방향 좌표, 가로 방향 좌표] 순으로 배열에 담아 return 하도록 solution 함수를 완성해주세요.
제한사항
park[i]
의 길이 ≤ 50
- S : 시작 지점
- O : 이동 가능한 통로
- X : 장애물
routes
의 길이 ≤ 50routes
의 각 원소는 로봇 강아지가 수행할 명령어를 나타냅니다.routes
의 첫 번째 원소부터 순서대로 명령을 수행합니다.routes
의 원소는 "op n"과 같은 구조로 이루어져 있으며, op는 이동할 방향, n은 이동할 칸의 수를 의미합니다.
- N : 북쪽으로 주어진 칸만큼 이동합니다.
- S : 남쪽으로 주어진 칸만큼 이동합니다.
- W : 서쪽으로 주어진 칸만큼 이동합니다.
- E : 동쪽으로 주어진 칸만큼 이동합니다.
1 ≤ n ≤ 9
입출력 예
park | routes | result |
---|---|---|
["SOO","OOO","OOO"] | ["E 2","S 2","W 1"] | [2,1] |
["SOO","OXX","OOO"] | ["E 2","S 2","W 1"] | [0,1] |
["OSO","OOO","OXO","OOO"] | ["E 2","S 3","W 1"] | [0,0] |
입출력 예 설명
입출력 예 #1
입출력 예 #2
입출력 예 #3
나의 풀이
package programmers;
import java.util.Arrays;
public class Walk {
public static int[] solution(String[] park, String[] routes) {
String[][] map = new String[park.length][park[0].length()];
int x = 0, y = 0;
for (int i = 0; i < park.length; i++) {
map[i] = park[i].split("");
for (int j = 0; j < map[i].length; j++) {
if (map[i][j].equals("S")) {
x = j;
y = i;
}
}
}
int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; // E, W, S, N in Cartesian coordinates
String dirStr = "EWSN";
for (String route : routes) {
String[] parts = route.split(" ");
char dirChar = parts[0].charAt(0);
int steps = Integer.parseInt(parts[1]);
int dirIndex = dirStr.indexOf(dirChar);
int tempY = y;
int tempX = x;
for (int i = 0; i < steps; i++) {
int newY = y + dirs[dirIndex][0];
int newX = x + dirs[dirIndex][1];
if (newX < 0 || newY < 0 || newX >= map[0].length || newY >= map.length || map[newY][newX].equals("X")) {
y = tempY;
x = tempX;
break;
} else {
y = newY;
x = newX;
}
}
}
int[] coordinate = new int[]{y, x};
return coordinate;
}
public static void main(String[] args) {
String[] park = {"OSO", "OOO", "OXO", "OOO"};
String[] routes = {"E 2", "S 3", "W 1"};
solution(park, routes);
}
}
나의 풀이
이 문제를 해결함에 있어,가장 먼저 고민했던 점이, park
, routes
를 보고 start 지점을 찾는 것이였다. 문제에 주어진 매개변수의 값을 가지고 start지점을 판단하는건데,
String[] park = {"OSO", "OOO", "OXO", "OOO"};
String[] routes = {"E 2", "S 3", "W 1"};
나는 map 이라는 2차원 배열을 선언하여, 문자 S와 같아 지는 지점의 i
,j
값을 활용하여 스타트지점의 좌표를 찾을 수 있었다.
String[][] map = new String[park.length][park[0].length()];
int x = 0, y = 0;
for (int i = 0; i < park.length; i++) {
map[i] = park[i].split("");
for (int j = 0; j < map[i].length; j++) {
if (map[i][j].equals("S")) {
x = j;
y = i;
}
}
}
그리고 이번 문제의 핵심 로직인 동서남북을 어떻게 나타낼것인가? 를 다음과 같이 나타내었다.
int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; // E, W, S, N in Cartesian coordinates
String dirStr = "EWSN";
우리가 흔히 아는 x,y의 좌표가 아닌, (y,x)
순서로 동(E), 서(W), 남(S), 북(N)을 나타내는데, 문제의 매개변수에서 E, S, W, N의 순서로 routes값을 주기때문에 초기값 설정이 매우 중요한 포인트다. 그리고 매개변수로 주어진 routes
를 가지고 어떻게 방향과 벡터 값을 나눌 것인가에 대한 나의 로직은 다음과 같다.
for (String route : routes) {
String[] parts = route.split(" ");
char dirChar = parts[0].charAt(0);
int steps = Integer.parseInt(parts[1])
routes를 for문 반복을 돌려, parts
라는 문자 배열에 띄어쓰기(" ")
를 기준으로 split
하여 담는다. 그리고 char형 dirChar에는 방향, int steps에는 벡터(크기) 를 담는다.
int dirIndex = dirStr.indexOf(dirChar);
해당 식은 String dirStr = "EWSN"
에서 indexOf
메서드를 활용하여, EWSN이라는 글자의 인덱스를 찾는다. 예를들어, 문제에서는 E,S,W 순서로 주어졌기때문에, EWSN 순서로 설정된 값에서 E : 0, S : 2, W : 1 번 index를 의미
한다.
int tempY = y;
int tempX = x;
for (int i = 0; i < steps; i++) {
int newY = y + dirs[dirIndex][0];
int newX = x + dirs[dirIndex][1];
if (newX < 0 || newY < 0 || newX >= map[0].length || newY >= map.length || map[newY][newX].equals("X")) {
y = tempY;
x = tempX;
break;
} else {
y = newY;
x = newX;
}
}
스텝( 벡터의 크기 ) 만큼 이동하면서, 임시 변수 x,y를 선언하는데 장애물이나, 범위를 초과할 시 임시 변수는 처음의 start 좌표로 돌아감을 의미한다. 즉, dirs[dirIndex][0]
,dirs[dirIndex][1]
의 의미는 2차원 배열 방향(동,서,남,북)을 의미한다.
int newY = y + dirs[dirIndex][0];
int newX = x + dirs[dirIndex][1];
그리고, 장애물 또는 이동할 수 있는 거리를 초과할 시, y,x
는 이동하지 않으며 이전의 위치 값으로 돌아가며, 반복을 종료 또는 정상적으로 이동을 하면 y, x 에 새로운 newY,newX
를 대입한다.
📑 문4) 어느 학교에 페인트가 칠해진 길이가 n미터인 벽이 있습니다. 벽에 동아리 · 학회 홍보나 회사 채용 공고 포스터 등을 게시하기 위해 테이프로 붙였다가 철거할 때 떼는 일이 많고 그 과정에서 페인트가 벗겨지곤 합니다. 페인트가 벗겨진 벽이 보기 흉해져 학교는 벽에 페인트를 덧칠하기로 했습니다.
넓은 벽 전체에 페인트를 새로 칠하는 대신, 구역을 나누어 일부만 페인트를 새로 칠 함으로써 예산을 아끼려 합니다. 이를 위해 벽을 1미터 길이의 구역 n개로 나누고, 각 구역에 왼쪽부터 순서대로 1번부터 n번까지 번호를 붙였습니다. 그리고 페인트를 다시 칠해야 할 구역들을 정했습니다.
벽에 페인트를 칠하는 롤러의 길이는 m미터이고, 롤러로 벽에 페인트를 한 번 칠하는 규칙은 다음과 같습니다.
롤러가 벽에서 벗어나면 안 됩니다.
구역의 일부분만 포함되도록 칠하면 안 됩니다.
즉, 롤러의 좌우측 끝을 구역의 경계선 혹은 벽의 좌우측 끝부분에 맞춘 후 롤러를 위아래로 움직이면서 벽을 칠합니다. 현재 페인트를 칠하는 구역들을 완전히 칠한 후 벽에서 롤러를 떼며, 이를 벽을 한 번 칠했다고 정의합니다.
한 구역에 페인트를 여러 번 칠해도 되고 다시 칠해야 할 구역이 아닌 곳에 페인트를 칠해도 되지만 다시 칠하기로 정한 구역은 적어도 한 번 페인트칠을 해야 합니다. 예산을 아끼기 위해 다시 칠할 구역을 정했듯 마찬가지로 롤러로 페인트칠을 하는 횟수를 최소화하려고 합니다.
정수 n, m과 다시 페인트를 칠하기로 정한 구역들의 번호가 담긴 정수 배열 section이 매개변수로 주어질 때 롤러로 페인트칠해야 하는 최소 횟수를 return 하는 solution 함수를 작성해 주세요.
제한사항
입출력 예
n | m | section | result |
---|---|---|---|
8 | 4 | [2,3,6] | 2 |
5 | 4 | [1,3] | 1 |
4 | 1 | [1,2,3,4] | 4 |
입출력 예 설명
입출력 예 #1
입출력 예 #2
1, 3번 영역에 페인트를 다시 칠해야 합니다. 롤러의 길이가 3미터이므로 한 번의 페인트칠에 연속된 3개의 구역을 칠할 수 있고 1, 2, 3번 영역에 페인트칠을 하면 한 번에 1, 3번 영역을 모두 칠할 수 있습니다.
따라서 최소 횟수인 1을 return 합니다.
입출력 예 #3
모든 구역에 페인트칠을 해야 합니다. 롤러의 길이가 1미터이므로 한 번에 한 구역밖에 칠할 수 없습니다. 구역이 4개이므로 각 구역을 한 번씩만 칠하는 4번이 최소 횟수가 됩니다.
따라서 4를 return 합니다.
나의 풀이
package programmers;
public class PaintOver {
public static int solution(int n, int m, int[] section) {
int roller = section[0];
int cnt = 1;
for(int i = 1; i < section.length; i++) {
if(roller + m - 1 < section[i]) {
cnt++;
roller = section[i];
}
}
return cnt;
}
public static void main(String[] args) {
solution(8, 4, new int[]{2,3,6});
}
}
나의 설명
roller
의 시작점을 section[0]
으로 잡는다. 즉, roller = 2; 가 되고, 기본적으로 무조건 1회이상은 덧칠을 해야하기에 cnt
의 초기값을 1로 둔다. roller의 범위를 roller + m -1
로 잡아 2~5까지 (크기 4) 를 잡아준다. 이때 section[i]
의 값 중,roller +m -1
보다 크면 이번 문제 에선 6이 해당하므로 cnt++; 결과인 2를 return 한다.
📑 문5) 고객의 약관 동의를 얻어서 수집된 1~n번으로 분류되는 개인정보 n개가 있습니다. 약관 종류는 여러 가지 있으며 각 약관마다 개인정보 보관 유효기간이 정해져 있습니다. 당신은 각 개인정보가 어떤 약관으로 수집됐는지 알고 있습니다. 수집된 개인정보는 유효기간 전까지만 보관 가능하며, 유효기간이 지났다면 반드시 파기해야 합니다.
예를 들어, A라는 약관의 유효기간이 12 달이고, 2021년 1월 5일에 수집된 개인정보가 A약관으로 수집되었다면 해당 개인정보는 2022년 1월 4일까지 보관 가능하며 2022년 1월 5일부터 파기해야 할 개인정보입니다.
당신은 오늘 날짜로 파기해야 할 개인정보 번호들을 구하려 합니다.
모든 달은 28일까지 있다고 가정합니다.
다음은 오늘 날짜가 2022.05.19일 때의 예시입니다.
약관 종류 | 유효기간 |
---|---|
A | 6달 |
B | 12달 |
C | 3달 |
번호 | 개입정보 수집 일자 | 약관 종류 |
---|---|---|
1 | 2021.05.02 | A |
2 | 2021.07.01 | B |
3 | 2022.02.19 | C |
4 | 2022.02.20 | C |
첫 번째 개인정보는 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
"약관 종류 유효기간"
형태의 약관 종류
와 유효기간
을 공백 하나로 구분한 문자열입니다.A
~Z
중 알파벳 대문자 하나이며, terms 배열에서 약관 종류
는 중복되지 않습니다.유효기간
은 개인정보를 보관할 수 있는 달 수를 나타내는 정수이며, 1 이상 100 이하입니다.1 ≤ privacies의 길이 ≤ 100
today와 privacies에 등장하는 날짜의 YYYY는 연도, MM은 월, DD는 일을 나타내며 점(.) 하나로 구분되어 있습니다.
파기해야 할 개인정보가 하나 이상 존재하는 입력만 주어집니다.
입출력 예
today | terms | privacies | result |
---|---|---|---|
"2022.05.19" | ["A 6", "B 12", "C 3"] | ["2021.05.02 A", "2021.07.01 B", "2022.02.19 C", "2022.02.20 C"] | [1,3] |
"2020.01.01" | ["Z 3", "D 5"] | ["2019.01.01 D", "2019.11.15 Z", "2019.08.02 D", "2019.07.01 D", "2018.12.28 Z"] | [1,4,5] |
입출력 예 설명
입출력 예 #2
약관 종류 | 유효기간 |
---|---|
Z | 3달 |
D | 5달 |
번호 | 개인정보 수지 일자 | 약관 종류 |
---|---|---|
1 | 2019.01.01 | D |
2 | 2019.11.15 | Z |
3 | 2019.08.02 | D |
4 | 2019.07.01 | D |
5 | 2018.12.28 | Z |
오늘 날짜는 2020년 1월1일입니다.
나의 풀이
package programmers;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class PersonalInformationCollectionValidityPeriod {
public static int[] solution(String today, String[] terms, String[] privacies) {
ArrayList<Integer> answerList = new ArrayList<>();
Map<String, String> mapList = new HashMap<>();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy.MM.dd");
for(String term : terms) {
String[] termSplit = term.split(" ");
mapList.put(termSplit[0],termSplit[1]);
}
System.out.println(mapList);
LocalDate todayDate = LocalDate.parse(today, dtf);
for(int i = 0; i < privacies.length; i++) {
String[] splitPrivacy = privacies[i].split(" ");
LocalDate date = LocalDate.parse(splitPrivacy[0],dtf);
for(Map.Entry<String, String> entry : mapList.entrySet()) {
if(splitPrivacy[1].contains(entry.getKey())){
date = date.plusMonths(Integer.parseInt(entry.getValue()));
date = date.minusDays(1);
if (date.getDayOfMonth() > 28) {
date = date.withDayOfMonth(28);
}
if(date.isBefore(todayDate)){
answerList.add(i + 1);
}
}
}
System.out.println(date);
}
int[] answer = new int[answerList.size()];
for (int i = 0; i < answer.length; i++) {
answer[i] = answerList.get(i);
}
System.out.println(Arrays.toString(answer));
return answer;
}
public 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"};
solution(today,terms, privacies);
}
}
나의 생각
내가 처음 생각 했던 로직은 String[] terms = {"A 6", "B 12", "C 3"}
를 Map의 key
,value
값으로 넣고, String[] privacies
Map의 key
값이 포함돼있으면, privacies
에 Map의 key
에 대응되는 value
값을 더하면 되겠다라고 생각했다. 하지만 년,월,일
을 어떻게 더할 것인가? 에 대한 로직을 구현하지 못해 구글링을 통해 자바에서는 날짜와 시간을 처리하는데 사용할 수 있는 다양한 클래스와 인터페이스가 있는 것을 알고 이를 써보기로 했다.
java.time.LocalDate
해당 클래스는 날짜를 표현하는데 사용되며, 시간과 시간대 정보를 가지고 있지 않음. 해당 클래스의 인스턴스(객체) 는 캘린더 시스템의 날짜 (YYYY-MM-DD)를 표현함.
LocalDate
클래스는 불변성을 가지므로, 한번 생성된 인스턴스는 변경되지 않음.LocalDate
클래스의 인스턴스는 일반적으로of
또는now
와 같은 팩토리 메서드를 통해 생성됨
LocalDate date = LocalDate.now(); // 현재 날짜 얻기
LocalDate specificDate = LocalDate.of(2023, Month.MAY, 31); // 특정 날짜 얻기
java.time.format.DateTimeFormatter
해당 클래스는 날짜와 시간 객체를 문자열로 변환하거나, 문자열을 날짜와 시간 객체로 변환하는데 사용됨.
DateTimeFormatter
의 인스턴스는 일반적으로ofPattern
메서드를 통해 생성되며, 다양한 패턴을 사용하여 날짜와 시간의 포맷을 정의할 수 있음
LocalDate date = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
String text = date.format(formatter); // "31-05-2023"와 같은 형식으로 날짜를 문자열로 변환
LocalDate parsedDate = LocalDate.parse(text, formatter); // 문자열을 다시 LocalDate 객체로 변환
나의 코드를 한 줄씩 리뷰하자면, 최종적으로 파기될 index를 저장할 answerList
, terms를 저정할 mapList
, 문자열을 날짜와 시간 객체로 변환하는데 사용할 DateTimeFormatter
의 dtf를 선언하였다.
그리고 향상된 for문을 통해 terms를 순회하여, split(" ")
메서드를 통해 띄어쓰기를 기준으로 문자를 분리 시켜 String[] termSplit
에 저장 한 뒤, mapList에 index[0], index[1] 을 mapList에 put
하는 방법을 사용하였다.
그리고, 매개변수 today
문자를 날짜 형식으로 변환하여 2022.05.19 -> 2022-05-19
날짜 폼으로 변환해주었다.
핵심 로직
for(int i = 0; i < privacies.length; i++) {
String[] splitPrivacy = privacies[i].split(" ");
LocalDate date = LocalDate.parse(splitPrivacy[0],dtf);
for(Map.Entry<String, String> entry : mapList.entrySet()) {
if(splitPrivacy[1].contains(entry.getKey())){
date = date.plusMonths(Integer.parseInt(entry.getValue()));
date = date.minusDays(1);
if (date.getDayOfMonth() > 28) {
date = date.withDayOfMonth(28);
}
if(date.isBefore(todayDate)){
answerList.add(i + 1); //index 0부터라서 +1
}
}
}
}
핵심 로직을 살펴보면, 개인정보인 privacies
를 순회하며 terms
를 띄어쓰기를 기준으로 분리시킨것 처럼 privacies
를 분리하여 날짜 폼에 해당하는 splitPrivacy[0]
을 dtf
의 형태로 변환하였다.
if(splitPrivacy[1].contains(entry.getKey()))
if(privacies[i].contains(entry.getKey()))
는 내가 원하는 값을 도출하는 방법의 차이가 있을 뿐 결과는 동일하다.
splitPrivacy[1]
인덱스는 문자인 A,B,C,D를 나타내기 때문에 contains
메서드나, equals
메서드를 써도 같은 결과가 나왔을 것이다. 그리고 privacies[i]
를 그대로 비교하여도, A,B,C,D... 값이 검출되기때문에 어떤것을 써도 무방하다. 내가 구현한 방법은
2021.05.02 A -> A 6
2021.05.02 A 문자열에 A가 포함되면 ~
으로 접근하였다.
Map 컬랙션의key
&value
값을 비교 할때는Map.Entry
인터페이스를 활용하면key
,value
값을 쉽게 비교할 수있다.
내가 고민 했던, 2021.05.02 에 06월을 어떻게 더할것인가? 에 해답이
LocalDate
클래스의 plusMonths, minusMonths ...(년, 달, 월 모두 가능) 를 사용하여 쉽게 구현할 수 있었다.
해당 문제에서는 모든 달은 28일로 고정돼있기때문에, 실제 날짜 출력에서 28이상 이면 date = date.withDayOfMonth
메서드를 활용하여 28일로 고정 시키는 방법을 사용하였다. 그리고 현재 날짜보다 . date 날짜 전이면 개인정보 유효기간이 지났다 라고 판단할 수 있기때문에 그 때의 index
를 answerList
에 저장하였다. 이 때 index는 0번부터 시작하기때문에, +1을 하여 문제에서 원하는 index 시작점을 1로 맞춰주었다.
최종적으로, int형 answer 배열을 선언하고, answerList를 answer에 넣어주는 방법으로 문제에서 원하는 리턴값의 형태로 결과를 도출할 수 있었다.