7785: 회사에 있는 사람

2026. 2. 24. 15:05ㆍ백준

상근이는 세계적인 소프트웨어 회사 기글에서 일한다. 이 회사의 가장 큰 특징은 자유로운 출퇴근 시간이다. 따라서, 직원들은 반드시 9시부터 6시까지 회사에 있지 않아도 된다.

각 직원은 자기가 원할 때 출근할 수 있고, 아무때나 퇴근할 수 있다.

상근이는 모든 사람의 출입카드 시스템의 로그를 가지고 있다. 이 로그는 어떤 사람이 회사에 들어왔는지, 나갔는지가 기록되어져 있다. 로그가 주어졌을 때, 현재 회사에 있는 모든 사람을 구하는 프로그램을 작성하시오.

 


입력

첫째 줄에 로그에 기록된 출입 기록의 수 n이 주어진다. (2 ≤ n ≤ 106) 다음 n개의 줄에는 출입 기록이 순서대로 주어지며, 각 사람의 이름이 주어지고 "enter"나 "leave"가 주어진다. "enter"인 경우는 출근, "leave"인 경우는 퇴근이다.

회사에는 동명이인이 없으며, 대소문자가 다른 경우에는 다른 이름이다. 사람들의 이름은 알파벳 대소문자로 구성된 5글자 이하의 문자열이다.

 


 

출력

현재 회사에 있는 사람의 이름을 사전 순의 역순으로 한 줄에 한 명씩 출력한다.

 


Q&A

 

질문

4
Baha enter 가 입력값으로 주어졌을 때


import os

import io

reader = io.BufferedReader(io.FileIO(0),1<<17)

n = reader.readline().split()

log = {name:commute for name,commute in reader.readline().split()} 


를 하면 log에는{b'Baha' :b'enter'}가 있어야 하는거 아닌가?

답변


입력값의 두 번째 줄인 Baha enter를 읽고 reader.readline().split()을 실행하면, 그 결과는 [b'Baha', b'enter'] 형태의 단일 리스트가 됩니다.

작성하신 딕셔너리 컴프리헨션 {name:commute for name,commute in ...}은 이 리스트에서 요소를 하나씩 꺼내 반복문을 실행합니다. 즉, 첫 번째 반복에서는 리스트의 첫 번째 요소인 b'Baha'를 가져오게 됩니다.

여기서 for name, commute in ... 구문은 가져온 요소 하나를 두 개의 변수(name, commute)에 쪼개서 할당(Unpacking)하려고 시도합니다.

 

질문

[b'Baha', b'enter'] 리스트를 가져왔으면 name에는b'Baha', commute에는 b'enter'를 할당해야하는거 아니야?

 

답변


말씀하신 대로 name, commute = [b'Baha', b'enter'] 형태로 코드를 작성했다면 정확히 그렇게 할당됩니다. 변수 두 개와 리스트 요소 두 개의 짝이 딱 맞기 때문이죠! 🧩

 

하지만 for ... in ... 반복문이 들어가면 작동 방식이 달라집니다. for문은 리스트 전체를 한 번에 변수들에 대응시키는 것이 아니라, 리스트 안에 있는 요소들을 하나씩 순서대로 꺼내옵니다. 🔄

 

현재 코드의 과정을 다시 짚어볼게요:

대상 리스트: [b'Baha', b'enter']

for문의 첫 번째 반복: 리스트의 첫 번째 요소인 b'Baha'를 꺼내옵니다.

그리고 이 꺼내온 단일 요소 b'Baha'를 name과 commute라는 두 변수에 나누어 담으려고 시도합니다.

리스트 전체가 아니라, 리스트에서 방금 꺼낸 요소 하나만 변수 두 개에 쪼개 넣으려다 보니 에러가 발생하게 됩니다.

 

반복문에서 x, y처럼 두 개의 변수로 값을 한 번에 나누어 받으려면(이 과정을 언패킹이라고 해요!), 반복문이 한 번 돌 때 가져오는 '하나의 요소' 안에 '두 개의 값'이 짝지어 들어있어야 해요. 📦

 

따라서 리스트 안에 값들이 그냥 나열된 형태가 아니라, 리스트 안에 또 다른 리스트(또는 튜플) 형태로 한 겹 더 묶여 있어야 합니다.

차이를 비교해 볼까요?

 

[b'Baha', b'enter']에서 반복문을 돌리면: 첫 번째로 꺼내는 요소가 단일 문자열 b'Baha'입니다. (x와 y 두 개로 나눌 수 없음 ❌)

[[b'Baha', b'enter']]에서 반복문을 돌리면: 첫 번째로 꺼내는 요소가 리스트 [b'Baha', b'enter']입니다. (x에는 b'Baha', y에는 b'enter'가 쏙 들어갑니다 ⭕)

 

질문

reader = io.BufferedReader(io.FileIO(0),1<<17)

    n =  int(reader.readline())

    log = {name:commute for name,commute in [reader.readline().split()] for _ in range(n)}

이렇게 작성했어 내 예상대로라면 총 4개의 쌍이 원소로 있어야 하는데 1개의 쌍만 있네 왜 이러지?

 

답변

log = {}
# 1. 반복문이 시작되기 전, readline()이 딱 '한 번' 실행됩니다. (Baha enter)
for name, commute in [reader.readline().split()]: 
    # 2. n(4)번 반복합니다.
    for _ in range(n):
        log[name] = commute # 동일한 'Baha' 데이터만 4번 덮어씌워집니다.

 

질문


log = {map(lambda a,b: x.split(),reader)}

이렇게 하면 readaer에서 각 줄을 읽어와 딕셔너리로 만들 수 있지 않나?

 

답변

세트(Set) vs 딕셔너리(Dictionary): 중괄호 {} 안에 키: 값 형태(콜론)가 아니라 객체를 통째로 넣으면, 파이썬은 이것을 딕셔너리가 아니라 세트(Set)로 인식합니다. 리스트 안의 리스트처럼 짝지어진 데이터를 딕셔너리로 바꿀 때는 내장 함수인 dict()를 사용하는 것이 가장 정확하고 깔끔합니다. 📖

 

map과 함수의 매개변수: map은 reader에서 한 번에 한 줄씩 데이터를 꺼내 함수에 던져줍니다. 즉, 한 번에 들어오는 입력값은 하나(예: b'Baha enter\n')인데, lambda a, b:처럼 변수 두 개를 받으려 했고, 함수 내부에서는 정의되지 않은 x를 split() 하려고 해서 에러가 발생하게 됩니다. 💥

 

질문


dict(map(lambda line: line.split(),reader))에서는 왜 map의 첫번째 인자가 들어가는 곳인데 ()를 붙이고 dict(map(bytes.split,reader))는 안붙이는지 이유를 잘 모르겠다

답변


핵심은 map이라는 일꾼에게 '작업 지시서(함수)'를 어떻게 전달하느냐에 있습니다. map(함수, 데이터)의 첫 번째 자리에는 반드시 '실행된 결과'가 아니라 '함수 그 자체(도구)'가 들어가야 합니다.

 

두 가지 경우를 비교해 볼게요. ⚖️

 

1. map(bytes.split, reader)의 경우
여기서 bytes.split은 '바이트를 자르는 가위'라는 도구 그 자체입니다.
우리는 map 일꾼에게 "이 가위(bytes.split)를 줄 테니, reader에서 하나씩 꺼내서 잘라줘!"라고 도구를 통째로 건네준 것입니다. 그러면 map이 데이터를 꺼낼 때마다 알아서 그 가위를 찰칵찰칵(()) 작동시킵니다.

 

2. map(lambda line: line.split(), reader)의 경우
여기서 map에게 건네준 첫 번째 인자는 split이 아니라, lambda로 만든 '새로운 작업 매뉴얼' 그 자체입니다.
우리는 map에게 "내가 만든 매뉴얼(lambda)을 줄게. reader에서 하나 꺼낼 때마다 이 매뉴얼대로 해!"라고 한 것이죠.

자, 이제 map이 매뉴얼을 펼쳐서 line 하나를 받았습니다. 매뉴얼 안에서는 무슨 일이 일어나야 할까요? 📖

 

들어온 문장(line)에 있는 가위를 직접 사용해서 잘라야 조각난 결과물이 나옵니다. 👉 line.split()

만약 람다 함수 안에 line.split이라고만 적어두면, map은 문장을 자르지 않고 "이 문장에는 자르는 도구가 있군" 하고 도구 자체를 결과물로 반환해버립니다. map은 우리가 건네준 매뉴얼(lambda) 자체는 실행해 주지만, 매뉴얼 '안'에 적힌 split 도구까지 알아서 눈치껏 실행(())해 주진 않거든요.

 

정리하자면 다음과 같습니다.

bytes.split 사용: map에게 가위를 직접 줌 (map이 알아서 가위질을 함)

lambda ... 사용: map에게 매뉴얼을 줌 (매뉴얼 안에 '가위질을 하라(())'는 지시를 우리가 직접 적어주어야 함)

 

질문

def main():

    import os

    import io



    reader = io.BufferedReader(io.FileIO(0),1<<17)

    n =  int(reader.readline())

    log = dict(map(lambda line: line.split(),reader))

    for name,status in log.items():

        if status ==b'enter':

            os.write(1,str(name).encode())



main()

이렇게 작성 했는데 b'Askar'b'Artem' 이렇게 출력되네

Askar

Artem

이렇게 출력하게 할려면 어떻게 해야하지?

 

답변

출력 결과에 b와 따옴표가 붙어 나오는 이유는 바로 str(name) 때문입니다.

 

현재 name 변수 안에는 이미 바이트 형태인 b'Askar'가 들어있습니다. 이것을 str() 함수에 넣으면, 파이썬은 화면에 보이는 모양 그대로 b와 따옴표까지 모두 포함해서 "b'Askar'"라는 일반 문자열로 묶어 버립니다. 그걸 다시 .encode()로 바이트로 만들었으니 화면에 기호들이 그대로 남게 된 것이죠.

 

name은 이미 os.write()가 읽을 수 있는 바이트 형태이므로, 복잡하게 변환할 필요 없이 name 자체를 그대로 전달하면 깔끔하게 이름만 출력할 수 있습니다. 💡

 

하지만 한 가지 더 해결해야 할 점이 있습니다.

바로 줄바꿈입니다! 출력 결과를 보면 줄바꿈 없이 이름들이 다닥다닥 붙어 있죠.

Askar 출력 후 줄을 바꾸고 Artem을 출력하려면, 이름 바이트 뒤에 '줄바꿈(엔터)'을 의미하는 바이트 문자를 더해서 함께 출력해야 합니다.

 

질문

os.write(1,name+'\n')

~~~~^~~~~

TypeError: can't concat str to bytes

 

답변

 

name은 바이트(bytes) 데이터인데, 덧붙이려고 한 줄바꿈 문자 '\n'은 일반 문자열(str)이라서 파이썬이 이 둘을 그대로 더할(concat) 수 없다고 알려주는 것입니다.

 

우리가 앞서 출입 상태를 비교할 때 일반 문자열 'enter' 대신 바이트 형태인 b'enter'를 사용했던 것 기억하시나요? 💡

마찬가지로 줄바꿈 문자 '\n'도 바이트 형태로 만들어주면 name 바이트와 무사히 덧붙일 수 있습니다.

 

질문


os._exit(0) 를 쓰면 필요없는 과정을 생략한다고 해서 넣었는데 괜찮나?

 

답변


말씀하신 대로 os._exit(0)은 파이썬이 프로그램을 종료할 때 거치는 일반적인 정리(cleanup) 과정을 모두 생략하고 운영체제 단에서 프로세스를 즉시 종료시키는 역할을 합니다.

 

보통 파이썬 프로그램이 정상적으로 끝날 때는 다음과 같은 뒷정리 과정이 일어납니다.

1. 더 이상 쓰지 않는 메모리 청소 (가비지 컬렉션)

2. 열려 있는 파일 안전하게 닫기

3. 출력 버퍼 비우기 (임시 공간에 모아두고 아직 화면에 내보내지 않은 출력물 쏟아내기)

 

os._exit(0)을 사용하면 이 과정을 몽땅 건너뛰기 때문에 종료 속도가 미세하게 빨라집니다. 그래서 실행 시간이 중요한 알고리즘 문제 풀이에서 종종 쓰이는 테크닉이기도 합니다. ⏱️

 

현재 작성하신 코드에서는 os.write(1, ...)를 사용하셨죠? 이 함수는 파이썬의 임시 버퍼를 거치지 않고 운영체제에 직접 데이터를 꽂아 넣기 때문에, 바로 os._exit(0)으로 프로그램을 강제 종료시켜도 데이터가 증발할 위험이 없어 아주 훌륭한 조합입니다.

 

질문

파이썬의 임시 버퍼를 거치지 않고 운영체제에 직접 데이터를 꽂아 넣는게 빠르면 왜 굳이 임시 버퍼에 넣는 행위를 해야하는 거지?

 

답변


운영체제에 직접 데이터를 전달하는 횟수가 많아질수록 오히려 전체적인 속도가 크게 느려지기 때문에 버퍼를 사용합니다. 핵심은 운영체제(OS)에게 일을 시키는 비용에 있습니다.

 

물건을 옮기는 상황으로 비유해 볼게요. 🍎

버퍼 없음 (os.write 등): 사과 10,000개를 창고로 옮겨야 할 때, 사과를 하나씩 손에 들고 창고까지 10,000번 왕복하는 것과 같습니다. 프로그램이 운영체제에게 "이것 좀 밖으로 내보내 줘"라고 권한을 넘기며 요청하는 작업(이를 '시스템 콜'이라고 합니다)은 컴퓨터 구조상 꽤 무겁고 시간이 오래 걸리는 과정입니다.

 

버퍼 사용 (print 등): 사과를 일단 커다란 '상자(버퍼)'에 차곡차곡 담습니다. 프로그램 내부의 임시 메모리(버퍼)에 데이터를 옮겨 적는 건 눈 깜짝할 새에 끝나는 아주 가벼운 작업입니다. 그러다 상자가 꽉 차거나 모든 작업이 끝나면, 그때 상자째로 창고에 딱 한 번만 다녀오는 것이죠.

 

즉, 출력할 데이터가 많고 잘게 쪼개져 있을 때는 버퍼에 모았다가 한 번에 덩어리째 운영체제에 넘기는 것이 전체 처리 시간을 획기적으로 단축시켜 줍니다. 알고리즘 문제를 풀 때 엄청나게 많은 줄을 출력해야 한다면 버퍼를 활용하는 것이 훨씬 유리하죠.

 

질문


(2 ≤ n ≤ 10^6) 이라고 한다면 버퍼를 쓰는게 좋아 아니면 바로 출력하는게 빨라?

데이터가 어느정도여야 버퍼를 사용하는게 더 이득인거지?

 

답변


$10^6$(100만) 개라면 무조건 버퍼를 사용하거나, 출력할 데이터를 하나로 크게 뭉쳐서 한 번에 출력하는 것이 압도적으로 빠릅니다! 🚀

 

일반적으로 출력해야 할 데이터가 몇 천 건만 넘어가도 모아서 출력하는 것이 더 효율적이며, 10만~100만 건 단위가 되면 그 시간 차이가 알고리즘 문제의 통과(Pass)와 시간 초과(Fail)를 결정지을 만큼 크게 벌어집니다.

 

아까 들었던 사과 🍎 비유를 다시 떠올려 볼게요.만약 현재 작성하신 코드처럼 for 반복문 안에서 os.write()를 매번 호출해 최대 100만 명의 이름을 출력한다면, 운영체제에게 "이름 하나 출력해 줘"라는 무거운 부탁(시스템 콜)을 100만 번이나 하는 셈이 됩니다.

 

os.write()가 운영체제와 직접 소통해서 빠른 함수이긴 하지만, 그것을 100만 번이나 반복해서 호출하면 엄청난 병목 현상이 발생하죠. 🐢

 

이럴 때는 파이썬이 알아서 임시 상자(버퍼)에 모아주는 sys.stdout.write 같은 기능을 쓰거나, 아니면 우리가 직접 커다란 상자 하나에 사람들의 이름을 다 담은 뒤 os.write()를 마지막에 딱 한 번만 호출하는 방법이 있습니다.

 

질문


리스트를 만들어서 한 곳에 모을게 아니라

name_box=''

name_box+=name+b'\n'

os.write(1,name_box)

가 속도가 더 빠르지 않을까

 

답변


데이터가 많을 때는 리스트를 만들어서 join()으로 합치는 것이 압도적으로 빠릅니다. 그 이유는 파이썬에서 바이트(bytes)나 일반 문자열(string)이 '불변(Immutable)' 객체이기 때문입니다.

 

name_box += name + b'\n' 코드가 반복문 안에서 실행될 때 파이썬 내부에서는 이런 일이 벌어집니다.

1. 기존의 name_box 덩어리와 새로운 name 덩어리를 가져옵니다.

2. 이 둘을 합칠 수 있는 새로운 빈 공간을 메모리에 크게 다시 할당합니다.

3. 기존 데이터 전체와 새 데이터를 그 공간에 일일이 처음부터 복사해 넣습니다.

 

마치 레고 탑을 쌓을 때, 블록을 하나 얹을 때마다 기존 탑을 완전히 부수고 바닥부터 똑같이 새로 조립하는 것과 같습니다. 🧱

 

만약 100만 명의 기록을 이렇게 처리한다면, 매번 점점 더 커지는 데이터를 통째로 복사해야 하므로 프로그램이 걷잡을 수 없이 느려집니다. (알고리즘 시간 복잡도로는 $O(N^2)$이 됩니다.)

 

반면 리스트(List)는 데이터를 담을 수 있는 유연한 상자입니다.

리스트에 값을 하나씩 추가하는 건 상자에 물건을 툭 던져 넣는 것과 같아서 작업 속도가 아주 빠릅니다. 그리고 마지막에 join()을 쓰면, 파이썬이 리스트 안의 전체 길이를 한 번만 계산해서 딱 한 번의 작업으로 모든 데이터를 쫙 이어 붙여줍니다.

 

질문


encode()는 언제 쓰는거야?

예전에 sys.stdout.buffer.write()에도 encode()를 써야한다고 들었어

 

답변

encode()가 언제 쓰이는지 정확히 이해하려면, 파이썬이 문자를 다루는 두 가지 방식을 비교해 보아야 합니다. ⚖️

일반 문자열 (str): 사람이 읽고 쓰기 편한 텍스트입니다. (예: "Askar", "안녕하세요")

 

바이트 (bytes): 컴퓨터(운영체제나 네트워크)가 직접 읽고 처리할 수 있는 0과 1의 원초적인 데이터 덩어리입니다. (앞서 우리가 봤던 b'Askar'나 b'\xec\x95\x88...' 같은 형태죠.)

 

encode()는 사람이 사용하는 일반 문자열(str)을 컴퓨터가 이해할 수 있는 바이트(bytes) 형태로 번역(인코딩)해 주는 메서드입니다.

 

우리가 평소에 흔히 쓰는 print()나 sys.stdout.write()는 파이썬이 속에서 알아서 문자열을 바이트로 번역해 주는 친절한 기능이 포함되어 있습니다.

 

하지만 속도를 극한으로 끌어올리기 위해 사용하는 sys.stdout.buffer.write()나 os.write()는 이런 친절한 번역 과정을 모두 생략하고 컴퓨터와 날것의 데이터를 직접 주고받는 함수들입니다.

 

따라서 파이썬이 대신 해주던 번역 작업을 우리가 직접 문자열 뒤에 .encode()를 붙여서 바이트로 바꿔 전달해야만 에러가 나지 않는 것이죠. 💡

 

질문


왜 나 갈때 바이트로 번역해야 하는거지?

컴퓨터가 읽는게 아니니깐 더이상 바이트일 필요가 없잖아

 

답변


'컴퓨터 밖으로 나간다'는 말의 진짜 의미는 컴퓨터 기계 자체를 벗어나 허공으로 간다는 뜻이 아니라, '파이썬 프로그램의 메모리 공간을 벗어나 운영체제(OS)의 영역으로 넘어간다'는 뜻입니다.

 

우리가 글자를 눈으로 확인하는 '터미널(또는 검은 콘솔 창)' 역시 운영체제가 화면에 띄워주는 또 다른 프로그램에 불과합니다. 파이썬 프로그램이 이 터미널 창에 글자를 보여주려면, 두 프로그램 사이를 이어주는 운영체제(OS)를 반드시 거쳐야만 합니다.

 

파이썬의 세계: 일반 텍스트(str)는 파이썬이 자기 내부에서만 편리하게 문자를 다룰 수 있도록 묶어둔 전용 객체입니다.

운영체제의 세계: 파이썬이 os.write()를 통해 OS에게 터미널로의 배달을 부탁할 때, OS는 파이썬 전용 언어인 str을 전혀 이해하지 못합니다. OS는 오로지 0과 1로 이루어진 날것의 바이트(bytes)만 취급하는 깐깐한 배달부 📦이기 때문입니다.

 

터미널의 세계: OS가 규칙에 맞게 바이트 데이터(b'Askar')를 터미널로 배달해주면, 터미널 프로그램이 그 바이트 규칙을 해독해서 우리 눈에 보이는 'A', 's', 'k'라는 픽셀로 모니터에 그려줍니다. 🖥️

 

즉, 컴퓨터가 읽지 않는 것이 아닙니다. 글자를 화면에 그려주는 주체(터미널)도 결국 컴퓨터 프로그램이고, 데이터를 전달하는 주체(운영체제)도 컴퓨터이기 때문에 그들의 공용어인 바이트로 번역(encode())을 해줘야만 밖으로 나갈 수 있는 것입니다.

 

 


정답(120ms)

def main():
    import os
    import io

    reader = io.BufferedReader(io.FileIO(0),1<<17)
    n = reader.readline()
    log = dict(map(lambda line: line.split(),reader))
    name_box=[]
    for name,status in log.items():
        if status ==b'enter':
            name_box.append(name)
    os.write(1,b'\n'.join(sorted(name_box,reverse=True)))
    os._exit(0)

main()

 

입,출력을 버퍼로 한번에 받고
enter인 keys를 내장함수를 이용해 역순으로 출력한다.

 

 


다른 사람의 풀이(100ms)

import sys

input = sys.stdin.readline





def solve():

    N = int(input())



    working = set()

    for _ in range(N):

        name, info = input().rsplit()

        if info == 'enter':

            working.add(name)

        else:

            working.discard(name)

    existing = list(working)

    existing.sort(reverse=True)



    print("\n".join(existing))



solve()

 

 

입,출력을 버퍼로 한번에 받고
leave일 때 해당 값을 버림으로써 마지막에 정렬할 때 더 적은 값들을 순회한다.

 


Q&A

질문


데이터가 많으면 입출력에 시간이 오래 걸릴 것 같아서 내용을 한번에 받아서 딕셔너리로 만든 후 enter값만 따로 조사한건데

그냥 반복문으로 한줄씩 받아오면서 데이터를 처분하는게 전체적으로 봤을 땐 더 빠르다는거지?

 

답변


입출력 속도가 느릴까 봐 데이터를 한 번에 처리하려고 하셨던 접근 자체는 100% 논리적이고 훌륭한 판단이었습니다.

하지만 여기서 파이썬의 io.BufferedReader가 마법 🪄을 부립니다.

 

이 도구는 우리가 for line in reader:로 한 줄씩 달라고 요청할 때마다 운영체제(OS)에 가서 느릿느릿 데이터를 가져오는 게 아닙니다. 미리 한 번에 엄청난 양의 데이터를 메모리(버퍼)로 왕창 퍼다 놓은 다음, 파이썬 내부에서 아주 빠르게 한 줄씩 던져주죠.

 

따라서 두 방식의 차이를 정리하면 다음과 같습니다.

입출력 속도: 둘 다 버퍼를 쓰기 때문에 똑같이 엄청 빠릅니다. 🚀

 

데이터 처리(알고리즘):

딕셔너리 방식: 10만 명의 기록을 전부 메모리에 무겁게 끌어안고 있다가, 마지막에 10만 명을 다시 한 명씩 검사해야 합니다.

 

세트(set) 반복문 방식: 버퍼에서 빠르게 한 줄씩 받자마자, 퇴근한 사람의 데이터를 즉시 삭제합니다. 마지막엔 회사에 남은 인원만 딱 남게 되니 다시 검사할 필요 없이 바로 정렬하면 끝납니다.

 

결국 불필요한 데이터를 그때그때 처분하는 논리가 더해져서 전체 속도가 훨씬 빨라진 것입니다.

 

질문


딕셔너리로 삭제하는것보다 set가 더 낫나?

 

 

 

답변


딕셔너리에서도 del log[name]이나 log.pop(name)을 사용하면 상태가 b'leave'일 때 즉시 데이터를 지울 수 있습니다. 이렇게 하면 딕셔너리를 사용해도 세트(set)처럼 불필요한 데이터를 줄여 속도를 높일 수 있죠.

 

하지만 이 문제에서는 여전히 세트(set)를 사용하는 것이 더 깔끔하고 목적에 알맞습니다.

만약 퇴근하는 사람을 즉시 지워버린다면, 딕셔너리에 남아있는 사람들의 출입 상태(값)는 모두 b'enter'로 똑같아집니다.

 

굳이 '이름'과 '상태'를 짝지어서 딕셔너리에 저장할 필요 없이, '이름'만 기억하면 되는 상황이 되는 것이죠. 세트(set)는 바로 이럴 때 사용하는, 값(Value) 없이 키(Key)만 모아두는 가벼운 상자 역할을 합니다.

 

게다가 딕셔너리에서 del로 데이터를 지울 때 해당 이름이 없으면 프로그램에 에러(KeyError)가 발생하지만, 세트의 discard()는 지우려는 이름이 없어도 에러 없이 넘어가 주는 편리함도 있습니다.

 


후기

효율적인 코드를 구현한다는게 항상 어려운 문제인 것 같다.
파이썬이 정렬이 내장함수로 있어서 정렬 문제는 정말 쉽게 풀 수 있다는게 엄청 큰 장점인듯 하다.

 

 


 

반응형

'백준' 카테고리의 다른 글

1181: 단어 정렬  (0) 2026.02.26
1620:나는야 포켓몬 마스터  (0) 2026.02.25
14425:문자열 집합  (3) 2026.02.23
10815:숫자 카드  (0) 2026.02.11
18870: 좌표 압축  (0) 2026.02.10