문제 보기 · Level 2 · 2019 KAKAO BLIND RECRUITMENT · 정답률 59% · 푼 사람 40,108명 (2026-09-30 기준)1
문제
채팅방 기록이 "Enter uid 닉네임", "Leave uid", "Change uid 닉네임" 꼴로 주어진다. 사용자는 uid로 구분한다. 닉네임이 바뀌면 이미 나온 메시지의 닉네임도 모두 새 닉네임으로 바뀐다. 모든 기록을 처리한 뒤 관리자가 보게 되는 "○○님이 들어왔습니다.", "○○님이 나갔습니다." 메시지 목록을 돌려준다.
- 기록 1~100,000개
- uid와 닉네임은 대소문자를 구분한다
풀이
막히면 한 단계씩 펼친다.
필요한 개념
딕셔너리와 집합: uid → 최종 닉네임 표.
1단계 · 작은 예로 손으로 풀기
기록 [“Enter u1 A”, “Enter u2 B”, “Change u1 C”, “Leave u2”]
- 들어오고 나간 사건만 메시지가 된다: u1 들어옴, u2 들어옴, u2 나감
- 마지막에 u1의 이름은 C, u2의 이름은 B다
- 그래서 화면은 [“C님이 들어왔습니다.”, “B님이 들어왔습니다.”, “B님이 나갔습니다.”]
메시지에 적힌 이름은 그때의 이름이 아니라 맨 마지막 이름이다.
2단계 · 가장 단순한 방법과 그 속도
메시지를 만들어 두고, 이름이 바뀔 때마다 지금까지의 메시지를 모두 훑어 고치는 방법이 있다. 기록이 10만 개면 최악에 10만 × 10만 = 100억 번이다. 안 된다.
3단계 · 핵심 아이디어
메시지에 이름을 바로 적지 말고 uid만 적어 둔다. 이름은 마지막에 한 번만 채운다.
- 첫 번째 훑기: uid → 이름 딕셔너리를 Enter와 Change 때마다 덮어쓴다. 끝나면 모두 최종 이름이다. 동시에 Enter·Leave 사건을 (uid, 들어옴/나감)으로 모은다.
- 두 번째 훑기: 모은 사건마다 딕셔너리에서 최종 이름을 꺼내 문장을 만든다.
떠올리는 신호: “나중에 바뀐 값이 앞의 결과에도 반영된다” → 결과를 마지막에 만들고, 그때 최종 값을 찾는다.
4단계 · 코드 흐름
- 빈 딕셔너리
name과 사건 리스트events를 만든다. - 기록마다 빈칸으로 나눈다. Enter면 이름을 적고 (uid, 들어옴)을 모은다. Leave면 (uid, 나감)을 모은다. Change면 이름만 적는다.
- 사건마다
name[uid]로 문장을 만든다.
5단계 · 파이썬 코드
def solution(record):
name = {} # uid → 최종 닉네임
events = [] # (uid, 들어옴/나감) 차례대로
for r in record:
parts = r.split()
action, uid = parts[0], parts[1]
if action in ("Enter", "Change"):
name[uid] = parts[2] # 이름은 계속 덮어쓴다
if action == "Enter":
events.append((uid, "들어왔습니다."))
elif action == "Leave":
events.append((uid, "나갔습니다."))
return [f"{name[uid]}님이 {msg}" for uid, msg in events]
parts[2]: Leave에는 닉네임이 없으니 Enter·Change일 때만 꺼낸다.action in ("Enter", "Change"): 둘 중 하나인지 본다.- 마지막 줄의 리스트 컴프리헨션이 두 번째 훑기다.
6단계 · 실행 추적
기록: Enter uid1234 Muzi / Enter uid4567 Prodo / Leave uid1234 / Enter uid1234 Prodo / Change uid4567 Ryan
| 기록 | name | events에 넣은 것 |
|---|---|---|
| Enter uid1234 Muzi | {1234: Muzi} | (1234, 들어옴) |
| Enter uid4567 Prodo | {1234: Muzi, 4567: Prodo} | (4567, 들어옴) |
| Leave uid1234 | 그대로 | (1234, 나감) |
| Enter uid1234 Prodo | {1234: Prodo, 4567: Prodo} | (1234, 들어옴) |
| Change uid4567 Ryan | {1234: Prodo, 4567: Ryan} | 없음 |
결과: Prodo 들어옴, Ryan 들어옴, Prodo 나감, Prodo 들어옴
7단계 · 복잡도와 흔한 실수
기록을 두 번 훑으니 \(O(n)\)이다.
- Change에도 메시지를 만든다. 이름 바꾸기는 메시지가 없다.
- Leave 기록에서 닉네임을 꺼내려다(
parts[2]) 오류가 난다. - 다시 들어올 때(Enter) 새 닉네임을 쓰는 것도 이름 바꾸기라는 점을 놓친다.
-
프로그래머스 스쿨, “오픈채팅방” (https://school.programmers.co.kr/learn/courses/30/lessons/42888), 2026-09-30 조회. ↩