[프로그래머스] 레벨2 오픈채팅방

0

🐶 코딩테스트

목록 보기
11/13
post-thumbnail

🔷 🖱️여기 클릭하면 문제로 이동해요!

이 글은 문제를 정리하고 저의 풀이를 보여드리는 거예요. 실제로 문제를 보고 풀어보세요. 클릭! 클릭! 해보세요! 🖱️

🔷 문제

채팅방에 들어오고 나가거나, 닉네임을 변경한 기록이 담긴 문자열 배열 record가 매개변수로 주어질 때, 모든 기록이 처리된 후, 최종적으로 방을 개설한 사람이 보게 되는 메시지를 문자열 배열 형태로 return 하도록 solution 함수를 완성하라.

카카오톡 오픈채팅방을 개설한 사람이 멤버들의 채팅방 '들어오기'와 '나가기'를 실시간으로 메세지로 보여주는 프로그램을 만드는 거예요. 그리고 멤버들이 닉네임을 바꾸면 기존의 메세지의 멤버 닉네임도 함께 바뀌게 만들어야 해요.

✅ 입출력예시

✅ 결과는 이렇게 나와야 하는 거예요.

출력은 이렇게 됩니다.

  • 누군가 채팅방에 들어왔을 때
    • "[닉네임]님이 들어왔습니다."
  • 누군가 채팅방을 나갔을 때
    • "[닉네임]님이 나갔습니다."
  • 닉네임이 바꾸는 방법
    • 채팅방을 나간 후, 새로운 닉네임으로 다시 들어간다.
    • 채팅방에서 닉네임을 변경한다.

❗ 예시

"Muzi님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Muzi님이 나갔습니다."

❗Muzi가 닉네임을 Peach로 변경하고 다시 들어왔어요.
    그럼 기존의 채팅방에 남아있는 닉네임 기록도 다 바껴요.

"Muzi님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Muzi님이 나갔습니다."


⬇️ (Muzi가 닉네임을 Peach로 바꾼후 다시 들어옴)
⬇️ (기존의 닉네임도 함께 바뀜)


"Muzi님이 들어왔습니다." ➡️ "Peach님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Muzi님이 나갔습니다." ➡️ "Peach님이 나갔습니다."


⬇️ (최종 표시되는 메시지)


"Peach님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Peach님이 나갔습니다."
"Peach님이 들어왔습니다."

만약 닉네임을 변경하고 다시 들어와도 기존의 닉네임 기록까지 바뀌지 않는다면 아래처럼 되었겠죠.

"Muzi님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Muzi님이 나갔습니다."


⬇️ (Muzi가 닉네임을 Peach로 바꾼후 다시 들어옴)
⬇️ (기존의 닉네임은 그대로, 새로 표시되는 메시지만 바뀐다면?)


"Muzi님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Muzi님이 나갔습니다."
"Peach님이 들어왔습니다."

❗채팅방에 남아있던 Prodo가 채팅방에서 닉네임을 Rion으로바꿨어요.
    Muzi처럼 Prodo도 기존의 닉네임 기록까지 모두 바뀝니다.

"Muzi님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Muzi님이 나갔습니다."


⬇️ (Muzi가 닉네임을 Peach로 바꾼후 다시 들어옴)
⬇️ (기존의 닉네임도 함께 바뀜)


"Muzi님이 들어왔습니다." ➡️ "Peach님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Muzi님이 나갔습니다." ➡️ "Peach님이 나갔습니다."


⬇️ (최종 표시되는 메시지)


"Peach님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Peach님이 나갔습니다."
"Peach님이 들어왔습니다."


⬇️ (Prodo가 채팅방에서 Rion으로 닉네임을 바꿈)
⬇️ (기존의 닉네임도 함께 바뀜)


"Peach님이 들어왔습니다."
"Prodo님이 들어왔습니다." ➡️ "Rion님이 들어왔습니다."
"Peach님이 나갔습니다."
"Peach님이 들어왔습니다."


⬇️ (최종 표시되는 메시지)


"Peach님이 들어왔습니다."
"Rion님이 들어왔습니다."
"Peach님이 나갔습니다."
"Peach님이 들어왔습니다."

✅ 입력은 이렇게 됩니다.

채팅방에 들어오고 나오거나, 닉네임을 변경한 기록이 담긴 문자열 배열 'record'

❗ 문자열 배열 record

길이는 1이상 100,000이하

❗ 배열 record에 담기는 문자열

  • 모든 유저는 유저 아이디로 구분 한다.

  • 유저 아이디 사용자가 닉네임 으로 채팅방에 입장
    • "Enter [유저 아이디][닉네임]"
      (ex. "Enter uid1234 Muzi")

  • 유저 아이디 사용자가 채팅방에서 퇴장
    • "Leave [유저 아이디]"
      (ex. "Leave uid1234")

  • 유저 아이디 사용자가 닉네임을 변경
    • "Change [유저 아이디][닉네임]"
      (ex. "Change uid1234 Muzi")

  • 첫 단어는 Enter, Leave, Change 중 하나이다.

  • 각 단어는 공백으로 구분되어 있으며, 알파벳 대문자, 소문자, 숫자로만 이루어져있다.

  • 유저 아이디와 닉네임은 알파벳 대문자, 소문자를 구별한다.
  • 유저 아이디와 닉네임의 길이는 1 이상 10 이하이다.
  • 채팅방을 나간 유저가 닉네임을 변경하는 등 잘못 된 입력은 주어지지 않는다.
    (채팅방을 나가서 닉네임을 변경하고 채팅방을 다시 들어오지도 않는데, 기존 메시지의 닉네임까지 변경되는 그런 것은 없다는 뜻이예요.)

🔷 내가 만든 로직

✅ HashMap을 사용하여 첫단어를 name으로 두번째단어를 value로 받는 함수

  • 입력되는 데이터들의 첫 단어는 "change, leave, enter"이고 두번째 단어는 "user ID"이고 세번째 단어는 "nickName"이다.
  • 이 때 Leave의 경우는 세번째 단어 "nickName"이 없다.
  • 그리고 HashMap의 자료구조는 집합이므로 동일한 데이터를 저장하지 않는다.
  • 그러므로 Leave의 경우를 제외하고 id를 name으로 nickName을 value로 HashMap에 저장해둔다.
  • id의 nickName이 최종으로 수정된 nickName으로 저장해두기 위해서이다.
  • 그리고 nickName을 출력할 때 이 HashMap에서 꺼내쓴다.

✅ 입력된 문자열 배열의 원소 한 개를 받아서 문제가 지시한대로 문자열 하나만 출력하는 함수

  • 문자열 배열의 원소 한 개를 받아서 첫번째 단어와 두번째 단어를 뽑아낸다.
  • 첫번째 단어에 따라서 문제가 지시한대로 Leave는 "nickName님이 나갔습니다.", Enter는 "nickName님이 들어왔습니다."라고 출력한다.
  • 이 때 nickName은 위의 HashMap에서 id의 value값, 즉 nickName을 꺼내 쓰면 된다.
  • 첫번째 단어가 Change일 경우 출력할 필요가 없으니 빈칸 하나를 일단 출력한다.

✅ 출력해야 하는 배열의 길이를 구하는 함수

  • 출력해야 하는 배열을 선언할 때 길이도 같이 선언해야 하므로 이 함수가 필요하다.
  • 입력된 문자열 배열을 향상된 for문을 이용하여 각 원소의 첫 단어가 Change일때는 출력할 문자열이 없으므로 이 때를 제외하고 answerLength의 값을 1씩 증가시킨다.

✅ 문제의 지시대로 문자열 배열을 출력하는 함수

  • 위의 함수를 통해 출력해야 할 문자열의 길이를 구한다.
  • 입력된 문자열의 길이 만큼 for문을 돌리는데
  • 입력된 문자열의 각 원소의 출력문자열을 return하는 함수를 이용한다.
  • 이때 return결과가 빈 칸 " "이 아니면 출력된 문자열 배열에 하나씩 집어넣는다.

✅ 위의 모든 함수들을 이용하여 최종 결과를 출력하는 함수

  • 먼저 id와 nickName을 정리해줄 첫번째 함수를 이용하고
  • 이 것의 결과 HashMap과 입력받은 문자열배열을 바로 위의 함수에 넣어주면 최종 결과가 출력된다.

🔷 내가 만든 코드

import java.util.HashMap;

class Solution {
    public HashMap<String, String> getIdNicknameMap(String[] record) {
        HashMap<String, String> idNicknameMap = new HashMap<>();

        for(String message : record) {
            String[] str = message.split(" ");
            //str[0] : Enter/Leave/Change  str[1] : 아이디  str[2] : 닉네임
            if(!str[0].equals("Leave"))
                idNicknameMap.put(str[1], str[2]);
        }

        return idNicknameMap;
    }

    public String getAnswer(String record, HashMap<String, String> idNickNameMap) {
        String action = record.split(" ")[0];
        String id = record.split(" ")[1];
        String nickname = idNickNameMap.get(id);

        switch (action) {
            case "Leave" : return nickname + "님이 나갔습니다.";
            case "Enter" :  return nickname + "님이 들어왔습니다.";
            case "Change" : default :  return " ";
        }
    }

    public int getAnswerLength(String[] record) {
        int answerLength = 0;

        for(String str : record) {
            String action = str.split(" ")[0];
            if(!action.equals("Change")) answerLength++;
        }

        return answerLength;
    }

    public String[] getAnswerArr(String[] record, HashMap<String, String> idNickNameMap) {
        int arrLength = getAnswerLength(record);
        String[] answerArr = new String[arrLength];

        for(int i = 0; i  < arrLength; i++) {
            String answer = getAnswer(record[i], idNickNameMap);
            if(!" ".equals(answer)) answerArr[i] = answer;
        }

        return answerArr;
    }

    public String[] solution(String[] record) {
        HashMap<String, String> idNicknameMap = getIdNicknameMap(record);

        String[] answer = getAnswerArr(record,idNicknameMap);
        return answer;
    }
    
}

🔷 나의 코드 결과 - 실패

테스트는 통과했지만 최종제출에서는 실패하였습니다.
테스트1부터 테스트32까지, 끝까지 모두 실패하였습니다.
이는 시간초과나 메모리초과가 이유가 아닌, 말 그대로 내가 만든 로직이 틀렸음을 의미합니다.

🔷 실패 요인 1 ➡️ null 출력

제가 만든 로직에서 getAnswer()함수는 입력받은 문자열의 첫 문자가 Change일 때 공백" "을 reutrn합니다. 그리고 이 return값을 getAnswerArr함수가 받았을 때 최종 결과로 출력될 문자열 배열에서 현재 index를 건너뛰어 그 다음 index로 넘어갑니다. 그래서 그 현재 index에는 null값이 들어가게 되는 것입니다.

🔷 실패 요인 2 ➡️ 무한루프

그래서 i--를 하면 출력될 문자열 배열에서 건너뛰게 되는 index가 없다고 판단했습니다.
그러나 for문에서 다시 1이 더해지고 그 다음 다시 1이 빠지고 하는 무한루프에 갖히게 되었습니다.

🔷 성공이유

입력되는 문자열 배열의 index와 출력되는 문자열 배열의 index를 서로 연관성이 전혀 없도록 완전히 분리 해내면 모든 문제가 해결됩니다.

🔷 내가 만든 코드

🖱️이 코드의 설명을 보고 싶다면 여기를 클릭해주세요!

import java.util.HashMap;

class Solution {
    public HashMap<String, String> getIdNicknameMap(String[] record) {
        HashMap<String, String> idNicknameMap = new HashMap<>();

        for(String message : record) {
            String[] str = message.split(" ");
            //str[0] : Enter/Leave/Change  str[1] : 아이디  str[2] : 닉네임
            if(!str[0].equals("Leave"))
                idNicknameMap.put(str[1], str[2]);
        }

        return idNicknameMap;
    }

    public String getAnswer(String record, HashMap<String, String> idNickNameMap) {
        String action = record.split(" ")[0];
        String id = record.split(" ")[1];
        String nickname = idNickNameMap.get(id);

        switch (action) {
            case "Leave" : return nickname + "님이 나갔습니다.";
            case "Enter" :  return nickname + "님이 들어왔습니다.";
            case "Change" : default :  return " ";
        }
    }

    public int getAnswerLength(String[] record) {
        int answerLength = 0;

        for(String str : record) {
            String action = str.split(" ")[0];
            if(!action.equals("Change")) answerLength++;
        }

        return answerLength;
    }

    public String[] getAnswerArr(String[] record, HashMap<String, String> idNickNameMap) {
        int arrLength = getAnswerLength(record);
        String[] answerArr = new String[arrLength];
        int answerArrIndex = 0;

        for(int recordIndex = 0; recordIndex  < record.length; recordIndex++) {
            String answer = getAnswer(record[recordIndex], idNickNameMap);
            if(!" ".equals(answer)) {
                if(recordIndex>0) answerArrIndex++;
                answerArr[answerArrIndex] = answer;
            }
        }

        return answerArr;
    }

    public String[] solution(String[] record) {
        HashMap<String, String> idNicknameMap = getIdNicknameMap(record);

        String[] answer = getAnswerArr(record,idNicknameMap);
        return answer;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
       
        String[] record = {"Enter user1 Muzi",
                "Enter user2 Prodo",
                "Leave user1",
                "Enter user1 Prodo",
                "Change user2 Ryan",
                "Leave user2",
                "Enter user2 HH",
                "Leave user2",
                "Enter user2 BB",
                "Change user1 WOW",
                "Change user1 NONONO"};
        String[] result = solution.solution(record);
        for(String line : result)
            System.out.println(line);
    }

}

🔷 나의 코드 결과 - 정답

테스트1부터 테스트32까지 모두 통과하였습니다!

profile
몇 번을 넘어져도 앞으로 계속 나아가자

0개의 댓글