Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 |
Tags
- 백준
- 탐색기법
- C++
- c++자료구조
- 재귀함수
- 구조체
- 언리얼로그
- permutation
- 프로그래밍
- 코딩테스트
- 코딩
- 링크드리스트
- 게임프로그래밍
- TPS
- 미라클모닝
- 구조체포인터
- fstring
- 오늘의에러
- UE5
- 언리얼
- 연산자오버로딩
- 내가해냄
- 개발
- 얌얌코딩
- 개발자
- unreal
- 게임개발
- 자료구조
- dfs
- 커스텀로그
Archives
- Today
- Total
All is well
[백준/C++] 7785(회사에 있는 사람) (문자열 비교 / Hash / unordered_map, unordered_set) 본문
C++/BAEKJOON
[백준/C++] 7785(회사에 있는 사람) (문자열 비교 / Hash / unordered_map, unordered_set)
D0YUN 2025. 3. 9. 10:26
https://www.acmicpc.net/problem/7785
내가 해냄
#include <bits/stdc++.h>
// #include <unordered_map> // 원래는 <bits/stdc++.h> 만 include해도 동작하는데 VS에서 동작하지 않아서 추가로 include
using namespace std;
long long n; // 출입 기록 수
unordered_map <string, string> syslog; // 출입 기록 로그 (이름 - 출입 상태 저장)
vector<string> cur; // 현재 회사에 있는 사람들 (정렬을 위해 사용)
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
// 출입 기록 수를 입력받는다
cin >> n;
// 출입 기록을 입력받는다
for (int i = 0; i < n; i++)
{
string tmp1, tmp2;
cin >> tmp1 >> tmp2;
syslog[tmp1] = tmp2; // 마지막 출입 상태만 저장됨 (enter / leave)
}
// 현재 회사에 있는 사람들을 기록한다
// unordered_map을 순회하여 "enter" 상태인 사람만 벡터에 추가
// *** string은 == 연산자 오버로딩이 되어 있어서 두 문자열이 같은지 비교 가능 ***
for (auto i : syslog)
if (i.second == "enter")
cur.push_back(i.first);
// 현재 회사에 있는 사람들을 역순으로 정렬한다
// *** greater<string>()을 비교자로 전달하여 사전 역순(내림차순)으로 정렬한다 ***
sort(cur.begin(), cur.end(), greater<string>());
// 회사에 있는 사람들을 출력한다
for (string i : cur)
cout << i << '\n';
return 0;
}
사용한 핵심 개념
// `unordered_map<string, string>` (해시 맵)
- 역할 : 사람의 이름(string)을 키(key)로 하고, 출입 상태("enter" 또는 "leave")를 값(value)으로 저장하는 자료구조다.
- 특징
- 평균 O(1)의 시간 복잡도로 빠른 검색, 삽입, 삭제가 가능하다.
- 마지막으로 기록된 출입 상태만 저장된다. (즉, 동일한 이름의 키가 등장하면 새로운 값으로 덮어씌워진다.)
- 사용 예시
unordered_map<string, string> syslog;
syslog["Alice"] = "enter"; // Alice가 회사에 들어옴
syslog["Alice"] = "leave"; // Alice가 퇴근 → 이전 값 "enter"가 덮어씌워짐
// `vector<string>`의 활용
- 역할 : 현재 회사에 남아 있는 사람들을 저장하고, 정렬 후 출력하는 역할을 한다.
- 사용 예시
vector<string> cur;
// `std::string`의 `==` 연산자 활용
- 역할: "enter"인지 "leave"인지 비교하여 현재 회사에 남아 있는 사람을 판별한다.
- 사용 코드
if (i.second == "enter")
cur.push_back(i.first);
- 작동 원리:
- C++의 `std::string` 클래스는 `==` 연산자가 오버로딩되어 있어 두 문자열이 같은지 쉽게 비교할 수 있다.
- C 스타일 문자열(char*)과 다르게 직접 비교가 가능하며, `strcmp()`를 사용할 필요가 없다.
- 예제
string a = "hello";
string b = "hello";
string c = "world";
if (a == b) cout << "a와 b는 같다.\n";
if (a != c) cout << "a와 c는 다르다.\n";
- 출력 결과
a와 b는 같다.
a와 c는 다르다.
- 시간 복잡도: O(N) (두 문자열의 길이가 N일 경우)
cf) `std::string::compare()` 함수
- 역할: `std::string::compare()`는 문자열 비교 결과를 정수 값으로 반환한다.
- 사용 예시
string a = "apple";
string b = "banana";
if (a.compare(b) == 0) cout << "같다.\n";
else if (a.compare(b) < 0) cout << "a가 b보다 사전순으로 앞에 있다.\n";
else cout << "a가 b보다 사전순으로 뒤에 있다.\n";
- 출력 결과
a가 b보다 사전순으로 앞에 있다.
- 비교 결과 값:
- 0 → 두 문자열이 같다.
- < 0 → 첫 번째 문자열이 사전순으로 앞에 있다.
- > 0 → 첫 번째 문자열이 사전순으로 뒤에 있다.
하지만 단순히 문자열이 같은지 비교할 때는 `==` 연산자를 사용하는 것이 더 직관적이고 간결하다.
// `sort()` + 비교 함수 `greater<string>()` (사전 역순 정렬)
- 역할: 회사에 남아 있는 사람들의 이름을 사전 역순(내림차순) 정렬하는 역할을 한다.
- 구현 방식:
sort(cur.begin(), cur.end(), greater<string>());
- 작동 원리:
- 기본적으로 sort()는 오름차순 정렬을 수행한다.
- `greater<string>()`을 비교 함수로 전달하면 내림차순 정렬을 수행하여 사전 역순으로 정렬된다.
- 사용 예시
vector<string> names = {"Alice", "Charlie", "Bob"};
sort(names.begin(), names.end(), greater<string>());
// 정렬 후: {"Charlie", "Bob", "Alice"} (사전 역순)
AI 피드백
#include <iostream>
#include <unordered_set>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
int n; // 출입 기록 수
cin >> n;
unordered_set<string> syslog; // 현재 회사에 있는 사람들 저장
for (int i = 0; i < n; i++) {
string name, action;
cin >> name >> action;
if(action == "enter") syslog.insert(name); // 입장 기록
else syslog.erase(name); // 퇴장 기록
}
// 출입 기록을 모두 처리한 후, 회사에 남아 있는 사람들을 벡터에 저장
vector<string> cur(syslog.begin(), syslog.end());
// 사전 역순(내림차순) 정렬
sort(cur.begin(), cur.end(), greater<string>());
// 결과 출력
for (const string& name : cur)
cout << name << '\n';
return 0;
}
초기 코드 핵심 로직 분석
초기 코드에서는 `unordered_map<string, string>`을 사용하여 이름을 키(key), 출입 상태를 값(value) 으로 저장하는 방식으로 구현하였다. 마지막으로 기록된 출입 상태만 저장되므로, 동일한 이름이 여러 번 등장해도 최종 상태만 유지가 된다.
출입 기록을 받은 후 "enter" 상태인 사람들만 벡터에 추가한 후, 사전 역순(내림차순)으로 정렬하여 출력하였다.
unordered_map<string, string> syslog; // (이름, 출입 상태)
for (int i = 0; i < n; i++) {
string name, action;
cin >> name >> action;
syslog[name] = action; // 마지막 출입 상태만 저장됨
}
// 현재 회사에 있는 사람들을 벡터에 추가
for (auto i : syslog)
if (i.second == "enter")
cur.push_back(i.first);
// 내림차순 정렬 후 출력
sort(cur.begin(), cur.end(), greater<string>());
for (string i : cur)
cout << i << '\n';
주요 개선점 : `unordered_set<string>`의 사용
- 초기 코드는 unordered_map<string, string>을 사용하여 {"이름", "enter"} 혹은 {"이름", "leave"} 형태로 저장하고 있지만, 사실 "leave"를 저장할 필요는 없다.
- 대신 `unordered_set<string>`을 사용하면 더 간결하고 직관적이며, O(1)의 탐색 속도로 더 빠르게 동작할 수 있다.
- 해당 자료형을 사용함으로써 개선된 내용은 다음과 같다
| 기존 코드 | 개선된 코드 |
| "leave" 상태를 따로 저장하여 비교 | "leave" 상태를 저장하지 않고, `erase()`로 삭제 |
| "enter" 상태를 벡터에 따로 저장 | `unordered_set`에서 `vector`로 변환 후 정렬 |
| 벡터를 따로 관리하며 "enter" 상태만 저장 | `unordered_set` 자체가 "enter" 상태인 사람만 저장 |
unordered_set<string> syslog; // 현재 회사에 있는 사람들 저장
for (int i = 0; i < n; i++) {
string name, action;
cin >> name >> action;
if (action == "enter") syslog.insert(name); // 입장 기록
else syslog.erase(name); // 퇴장 기록
}
// 출입 기록을 모두 처리한 후, 벡터로 변환
vector<string> cur(syslog.begin(), syslog.end());
// 내림차순 정렬 후 출력
sort(cur.begin(), cur.end(), greater<string>());
for (const string& name : cur)
cout << name << '\n';
추가로 사용한 개념
// unordered_map → unordered_set로 변경
- 기존 코드 문제점:
- `unordered_map`을 사용하여 "enter"와 "leave" 값을 저장했으나, 사실 "leave" 상태는 따로 저장할 필요가 없다.
- 회사를 떠난 사람은 최종 결과에서 출력하지 않기 때문이다.
- `unordered_map`을 사용하여 "enter"와 "leave" 값을 저장했으나, 사실 "leave" 상태는 따로 저장할 필요가 없다.
- 개선 코드 장점:
- "enter"일 때는 insert(), "leave"일 때는 `erase()`를 사용하여 바로 추가 및 삭제가 가능하다
- 불필요한 "leave" 값 비교 연산을 제거하여 연산 속도가 평균 O(1)으로 개선된다
unordered_set<string> syslog;
if (action == "enter") syslog.insert(name);
else syslog.erase(name);
// 벡터 변환 후 정렬 (vector<string> cur(syslog.begin(), syslog.end());)
- 기존 코드 문제점:
- "enter" 상태인 사람을 벡터에 따로 추가하는 과정이 필요했다
- 개선 코드 장점:
- `unordered_set`을 벡터의 생성자로 변환하여 한 번에 저장할 수 있다.
- `vector<string> cur(syslog.begin(), syslog.end());`를 사용하여 O(N) 시간 내 변환할 수 있다.
이분 탐색이나 투 포인터 방식을 이용하여 구현할 수도 있다고 한다