알고리즘/프로그래머스

[프로그래머스/JAVA] 오픈채팅방 - 2019 KAKAO BLIND RECRUITMENT

Heony 2022. 1. 30. 13:21

2019 KAKAO BLIND RECRUITMENT LEVEL 2

오픈채팅방의 문제는 다음과 같이 이해하면 된다.

'111', '222'라는 닉네임을 가진 사람이 채팅방에 들어오고 나가는 과정을 예시로 보여주자면 다음과 같다.

111님이 들어왔습니다.
222님이 들어왔습니다.
111님이 나갔습니다.

 

다만 여기서 '111'이라는 친구가 '333'이라는 닉네임으로 바꾸어서 들어오면 다음과 같이 출력문이 바뀌어야 한다.

333님이 들어왔습니다.
222님이 들어왔습니다.
333님이 나갔습니다.
333님이 들어왔습니다.

이전에 있었던 모든 공지가 '333'이라는 닉네임으로 바뀌어야 하며, 333이라는 닉네임으로 들어왔으니 들어온 공지를 내야한다는 의미이다. 즉 여기에서 알 수 있는 사실들을 몇 가지 뽑아보자.

 

첫째, 이전의 기록은 상관이 없다.
111이라는 닉네임이 소멸되었다. 우리는 지속적으로 데이터 값을 저장하여 이전의 history를 남겨야 할 필요가 없다는 뜻이다. 기존 아이디만 알고있으면 닉네임은 덮어씌워도 된다는 의미가 되겠다. 따라서 닉네임을 찾는데 시간도 줄이고, 덮어씌울 수 있는 기능을 가진 map을 활용하여 아이디와 이름을 관리할 것이다.

둘째, 나갔다고 해서 map을 지우면 안 된다.
나감으로 인해서 map을 지운다는 것은 이후에 어떤 닉네임으로 들어왔는지 체크할 수도 없으며, 공지사항을 담을 list에서도 아이디 값만 저장될 뿐 어떤 map을 참조해야 하는지에 대한 의문이 생긴다. 따라서 우리는 모든 사항들을 지우지 않고 덮어씌우면서 코딩을 진행할 것이다.

 


선언문

        Map<String, String> map = new HashMap<String, String>();
        List<String> notice = new ArrayList<String>();
        Set<String> dup = new HashSet<String>();

map은 아이디와 닉네임이 저장될 변수이다. map은 remove 메소드를 사용하지 않으며, 닉네임이 중복되어서 나오는 경우에는 덮어씌워 이전의 history도 이름을 바꿀 수 있도록 할 것이다.

notice는 Change를 제외한 Enter, Leave에 관해서만 어떤 아이디가 이벤트를 열었는지 확인하는 용도이다.

dup은 이후에 출력할 때 사용된다.

 

주요 알고리즘

        for(int i = 0; i < record.length; i++) {
            String[] str = record[i].split(" ");
            if(str[0].equals("Leave")) {
                notice.add(str[1]);
            } else if(str[0].equals("Enter")) {
                map.put(str[1], str[2]);
                notice.add(str[1]);
            } else {
                map.put(str[1], str[2]);
            }
        }
"Enter uid1234 Muzi", 
"Change uid4567 Ryan"
"Leave uid1234"

다음과 같이 Enter와 Change는 세 단락으로 구성되었지만, Leave는 두 단락으로 구성되어 있다.

첫 번째 if문에서 Leave를 걸러내는데 map에는 영향을 미치지 않는다. 이는 remove할 경우 누가 들어왔었는지에 대한 공지가 애매해지므로 map에 영향을 미치지 않는 것이며, notice로 아이디만 저장시켜 후에 참조할 것이다.

두 번째 if문에서는 Enter을 걸러내는데 들어온 것을 의미하므로 아이디, 닉네임으로 map에 저장시키고, notice에 어떤 아이디가 들어왔는지에 대한 공지를 저장한다.

세 번째 else문에서는 Change를 걸러내는데 바뀐 것에 대해서는 공지를 할 필요가 없이 덮어씌우기만 하면 되므로 map.put을 사용한다.

 

출력

        String[] answer = new String[notice.size()];

        for(int i = 0; i < notice.size(); i++) {
            String name = notice.get(i);
            if(dup.add(name)) {
                answer[i] = map.get(notice.get(i)) + "님이 들어왔습니다.";
            } else {
                answer[i] = map.get(notice.get(i)) + "님이 나갔습니다.";
                dup.remove(name);
            }
        }

        return answer;

이제 notice라는 공지 안에 아이디 값만 들어온 순서대로 저장되어 있을 것이다. 따라서 for구문을 통해 반복시켜 어떤 아이디가 저장되어 있는지 추출한 다음 그것을 판단할 것이다.

dup의 존재의미는 여기서 탄생한다.
예를 들어 어떤 아이디 값이 나오고, 그 이후에 그 아이디 값이 또 나온다고 가정하면 그것은 '아이디가 들어왔다가 다시 나갔다'라는 의미로 판단할 수 있다. 결국 set에 의해 중복되는 아이디가 나왔다는 것은 나갔다는 것을 의미하고 set에서 제거시켜 아이디 값이 다시 존재하지 않게끔 만들어 둔다. 이를 반복하여 answer에는 정확한 log값만 찍히게 된다.

 

코드

import java.util.*;
class Solution {
    public String[] solution(String[] record) {
        Map<String, String> map = new HashMap<String, String>();
        List<String> notice = new ArrayList<String>();
        Set<String> dup = new HashSet<String>();

        for(int i = 0; i < record.length; i++) {
            String[] str = record[i].split(" ");
            if(str[0].equals("Leave")) {
                notice.add(str[1]);
            } else if(str[0].equals("Enter")) {
                map.put(str[1], str[2]);
                notice.add(str[1]);
            } else {
                map.put(str[1], str[2]);
            }
        }

        String[] answer = new String[notice.size()];

        for(int i = 0; i < notice.size(); i++) {
            String name = notice.get(i);
            if(dup.add(name)) {
                answer[i] = map.get(notice.get(i)) + "님이 들어왔습니다.";
            } else {
                answer[i] = map.get(notice.get(i)) + "님이 나갔습니다.";
                dup.remove(name);
            }
        }

        return answer;
    }
}

728x90