
문제 설명
카카오톡 오픈채팅방에서는 친구가 아닌 사람들과 대화를 할 수 있는데, 본래 닉네임이 아닌 가상의 닉네임을 사용하여 채팅방에 들어갈 수 있다.
신입사원인 김크루는 카카오톡 오픈 채팅방을 개설한 사람을 위해, 다양한 사람들이 들어오고, 나가는 것을 지켜볼 수 있는 관리자창을 만들기로 했다. 채팅방에 누군가 들어오면 다음 메시지가 출력된다.
"[닉네임]님이 들어왔습니다."
채팅방에서 누군가 나가면 다음 메시지가 출력된다.
"[닉네임]님이 나갔습니다."
채팅방에서 닉네임을 변경하는 방법은 다음과 같이 두 가지이다.
1. 채팅방을 나간 후, 새로운 닉네임으로 다시 들어간다.
2. 채팅방에서 닉네임을 변경한다.
닉네임을 변경할 때는 기존에 채팅방에 출력되어 있던 메시지의 닉네임도 전부 변경된다.
예를 들어, 채팅방에 "Muzi"와 "Prodo"라는 닉네임을 사용하는 사람이 순서대로 들어오면 채팅방에는 다음과 같이 메시지가 출력된다.
"Muzi님이 들어왔습니다."
"Prodo님이 들어왔습니다."
채팅방에 있던 사람이 나가면 채팅방에는 다음과 같이 메시지가 남는다.
"Muzi님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Muzi님이 나갔습니다."
Muzi가 나간후 다시 들어올 때, Prodo 라는 닉네임으로 들어올 경우 기존에 채팅방에 남아있던 Muzi도 Prodo로 다음과 같이 변경된다.
"Prodo님이 들어왔습니다."
"Prodo님이 들어왔습니다."
"Prodo님이 나갔습니다."
"Prodo님이 들어왔습니다."
채팅방은 중복 닉네임을 허용하기 때문에, 현재 채팅방에는 Prodo라는 닉네임을 사용하는 사람이 두 명이 있다. 이제, 채팅방에 두 번째로 들어왔던 Prodo가 Ryan으로 닉네임을 변경하면 채팅방 메시지는 다음과 같이 변경된다.
"Prodo님이 들어왔습니다."
"Ryan님이 들어왔습니다."
"Prodo님이 나갔습니다."
"Prodo님이 들어왔습니다."
채팅방에 들어오고 나가거나, 닉네임을 변경한 기록이 담긴 문자열 배열 record가 매개변수로 주어질 때, 모든 기록이 처리된 후, 최종적으로 방을 개설한 사람이 보게 되는 메시지를 문자열 배열 형태로 return 하도록 solution 함수를 완성하라.
제한사항
1. record는 다음과 같은 문자열이 담긴 배열이며, 길이는 1 이상 100,000 이하이다.
2. 다음은 record에 담긴 문자열에 대한 설명이다.
2-1. 모든 유저는 [유저 아이디]로 구분한다.
2-2. [유저 아이디] 사용자가 [닉네임]으로 채팅방에 입장 - "Enter [유저 아이디] [닉네임]" (ex. "Enter uid1234 Muzi")
2-3. [유저 아이디] 사용자가 채팅방에서 퇴장 - "Leave [유저 아이디]" (ex. "Leave uid1234")
2-4. [유저 아이디] 사용자가 닉네임을 [닉네임]으로 변경 - "Change [유저 아이디] [닉네임]" (ex. "Change uid1234 Muzi")
2-5. 첫 단어는 Enter, Leave, Change 중 하나이다.
2-6. 각 단어는 공백으로 구분되어 있으며, 알파벳 대문자, 소문자, 숫자로만 이루어져있다.
2-7. 유저 아이디와 닉네임은 알파벳 대문자, 소문자를 구별한다.
2-8. 유저 아이디와 닉네임의 길이는 1 이상 10 이하이다.
2-9. 채팅방에서 나간 유저가 닉네임을 변경하는 등 잘못 된 입력은 주어지지 않는다.
입출력 예시
record | result |
---|---|
["Enter uid1234 Muzi", "Enter uid4567 Prodo","Leave uid1234","Enter uid1234 Prodo","Change uid4567 Ryan"] | ["Prodo님이 들어왔습니다.", "Ryan님이 들어왔습니다.", "Prodo님이 나갔습니다.", "Prodo님이 들어왔습니다."] |
풀이 코드
문제가 길고 제한사항이 복잡한 것 같지만 주제가 그렇게 어렵지 않아서 이해하는데 어려움은 없는 문제.
주어진 테스트 케이스 진행사항을 적어보면 아래와 같다.
[Muzi] 입장
Muzi님이 들어왔습니다. 출력
[Prodo] 입장
Prodo님이 들어왔습니다. 출력
[Muzi] 퇴장
Muzi님이 나갔습니다. 출력
[Muzi] -> [Prodo] 로 닉네임 변경 후 입장
전체 출력문 갱신
Prodo님이 들어왔습니다. (갱신)
Prodo님이 들어왔습니다.
Prodo님이 나갔습니다. (갱신)
Prodo님이 들어왔습니다.
[원래 Prodo 였던 유저] -> [Ryan] 로 닉네임 변경
전체 출력문 갱신
Prodo님이 들어왔습니다.
Ryan님이 들어왔습니다. (갱신)
Prodo님이 나갔습니다.
Prodo님이 들어왔습니다.
직관적으로 생각나는 방법은 유저가 채팅방에 입장 또는 퇴장할 때 로그에 담고, 닉네임 변경 시 로그에서 원래 닉네임을 찾아낸 후 갱신하는 방법이 있을텐데, record 길이가 최대 100,000 까지 주어지기 때문에 어떤 유저가 어떤 행동을 했는지 일일히 찾아내는 과정에서 시간 초과가 발생하게 된다.
중요한건 닉네임을 몇 번 바꾸든 제일 마지막에 바꾼 값으로 로그 값이 갱신되는 것이다. 위에 적어놨듯이 어떤 유저가 닉네임을 바꿀 때 마다 로그를 순환하며 값을 갱신하는 것은 굉장히 비효율적이기 때문에 저장된 로그 자체에서 닉네임을 찾아 갱신하는 것 보다 딕셔너리를 생성해 닉네임을 바꿀 때 마다 딕셔너리의 값을 변경하는 것은 어떨까? key 값은 uid 로 하고 value 값을 nickname 으로 설정한 뒤 모든 유저 행동에 맞춰 값을 갱신한다면 ? 만약 이렇게 하면 주어진 테스트 케이스를 예로 들었을 때 최종적으로 저장되는 값은 다음과 같다.
{'uid1234': 'Prodo', 'uid4567': 'Ryan'} |
여기까지 코드는 아래와 같다.
Leave 가 sign 에 할당되면 nickname 은 따로 주어지지 않으니까 분기문이 필요하다.
def solution(records):
logs = []
answer = []
record_dict = dict()
for record in records:
split = record.split()
sign = split[0]
uid = split[1]
nickname = ''
# Leave 가 아닌 경우 nickname 변수 값 할당
if sign != 'Leave':
nickname = split[2]
# 채팅방에 접속한 유저 딕셔너리에 저장
if sign == 'Enter':
record_dict[uid] = nickname
# 바뀐 닉네임이 있다면 uid key 에 매칭되는 nickname vlaue 갱신
if sign == 'Change':
record_dict[uid] = nickname
... 중략
하지만 반환 목표는 모든 유저의 채팅방 기록이지 마지막으로 갱신된 닉네임이 무엇인가를 반환하는 것은 아니다. 이 딕셔너리를 활용하기 위해선 생각의 전환이 조금 필요한데, Muzi님이 들어왔습니다. 가 아니라 uid1234님이 들어왔습니다. 라고 바꿔서 생각해보자. 이렇게 생각하면 해당 uid 에 마지막으로 갱신된 최신 닉네임이 딕셔너리에 매칭되어있기 때문에 uid 부분을 nickname 으로 교체해주면 된다 !
최종 코드는 아래와 같다.
채팅방 진행상황은 파악해둬야하니 logs 리스트에 uid + , + 님이 들어왔습니다. or 나갔습니다. 형식으로 저장해두고 logs 를 순회하면서 현재 uid 와 매핑되어있는 최신 nickname 으로 치환하기만 하면 끝. (split 을 위해 , 를 껴놨기 때문에 , 도 함께 공백으로 치환)
def solution(records):
logs = []
answer = []
record_dict = dict()
for record in records:
split = record.split()
sign = split[0]
uid = split[1]
nickname = ''
# Leave 가 아닌 경우 nickname 변수 값 할당
if sign != 'Leave':
nickname = split[2]
# 채팅방에 접속한 유저 딕셔너리에 저장
# uid 를 파악해서 nickname 으로 교체하기 위해 uid 님이 들어왔습니다. 형식으로 저장
if sign == 'Enter':
record_dict[uid] = nickname
logs.append(uid + ',' + '님이 들어왔습니다.')
# uid 를 파악해서 nickname 으로 교체하기 위해 uid 님이 나갔습니다. 형식으로 저장
if sign == 'Leave':
logs.append(uid + ',' + '님이 나갔습니다.')
# 바뀐 닉네임이 있다면 uid key 에 매칭되는 nickname vlaue 갱신
if sign == 'Change':
record_dict[uid] = nickname
# answer 에 저장된 uid 를 현 상태의 nickname 으로 교체하면 끝.
for log in logs:
split = log.split(',')
uid = split[0]
answer.append(log.replace(uid, record_dict[uid]).replace(',', ''))
return answer
'알고리즘 정리' 카테고리의 다른 글
파이썬(Python) 알고리즘 문제풀이 베스트앨범 [프로그래머스 / Hash] (0) | 2023.07.31 |
---|---|
파이썬(Python) 알고리즘 문제풀이 베스트앨범 [프로그래머스 / Hash] (0) | 2023.07.24 |
파이썬(Python) 알고리즘 문제풀이 의상(위장) [프로그래머스 / Hash] (0) | 2023.07.12 |
파이썬(Python) 알고리즘 문제풀이 전화번호 목록 [프로그래머스 / Hash] (1) | 2023.07.11 |
파이썬(Python) 알고리즘 문제풀이 완주하지 못한 선수 [프로그래머스 / Hash] (0) | 2023.07.11 |