Problem SolvingSep 3, 2025
[프로그래머스] 150366 표 병합 (C/C++)
![[프로그래머스] 150366 표 병합 (C/C++)](/assets/posts/01k4778fbr2dtmbapkv7ha2fmv/index.png)
문제
https://school.programmers.co.kr/learn/courses/30/lessons/150366
풀이
문제를 처음 봤을 때는 일종의 DBMS를 구현하는 문제라고 생각했다.
그래서 처음에는 B-Tree 계열 자료구조를 활용하고, vector와 map을 hybrid container로 조합해 CRUD를 O(log N)으로 처리할 수 있도록 설계했다.
하지만 표의 크기가 50x50 이고 명령어의 개수가 1000개 이하인 것을 보고 시간 복잡도에 관련된 문제는 아니구나 생각하고 다시 단일 vector container로 구현했다.
MERGE 명령어를 지원해야한다는 점에서 바로 Union-Find 알고리즘이 떠올랐다.
다만 UNMERGE 명령어를 구현할 때 어려움이 있었는데, Union-Find 특성상 모든 노드가 항상 최상위 부모를 직접 가리키고 있지 않기 때문에 특정 그룹을 찾는게 어려웠다.
하지만 표 크기가 작은 덕분에 단순히 모든 셀을 순회하면서 같은 부모를 가지는 셀을 찾아 분리하는 방식으로 해결했다.
알고리즘 자체는 어렵지 않지만, 구현해야 할 기능이 많아서 시간이 많이 소요되는 문제였다.
메모
- Segmentation fault, abort 등의 오류가 발생하면 UB 혹은 배열 크기를 확인하자.
- 구조적 바인딩으로 선언된 변수는 람다에서 캡쳐할 수 없다.
소스 코드
C++17
#include <bits/stdc++.h>
#define SIZE 51
typedef std::vector<std::string> Record;
typedef std::vector<Record> Table;
typedef std::vector<std::string> Args;
typedef std::function<void(const Args &)> Handler;
typedef std::pair<size_t, size_t> Coordinate;
Table table(SIZE, Record(SIZE, ""));
std::vector<std::vector<Coordinate>> parent(SIZE, std::vector<Coordinate>(SIZE));
Args split(const std::string &source) {
Args ret;
std::string token;
std::istringstream iss(source);
while (iss >> token) {
ret.push_back(token);
}
return ret;
}
Coordinate find(const Coordinate ¤t) {
const auto [ r, c ] = current;
if (parent[r][c] == current) {
return current;
}
return parent[r][c] = find(parent[r][c]);
};
template <typename Func>
void forEach(Func &&f) {
for (size_t i = 1; i <= 50; ++i) {
for (size_t j = 1; j <= 50; ++j) {
f(i, j);
}
}
}
std::vector<std::string> solution(std::vector<std::string> commands) {
std::vector<std::string> answer;
forEach([&] (size_t i, size_t j) {
parent[i][j] = { i, j };
});
Handler update = [&] (const Args &args) {
if (args.size() == 3) {
const size_t r = std::stoul(args[0]);
const size_t c = std::stoul(args[1]);
const auto [ pr, pc ] = find({ r, c });
table[pr][pc] = args[2];
} else {
forEach([&] (size_t i, size_t j) {
if (table[i][j] == args[0] && Coordinate{ i, j } == find({ i, j })) {
table[i][j] = args[1];
}
});
}
};
Handler merge = [&] (const Args &args) {
const size_t r1 = std::stoul(args[0]);
const size_t c1 = std::stoul(args[1]);
const size_t r2 = std::stoul(args[2]);
const size_t c2 = std::stoul(args[3]);
if (r1 == r2 && c1 == c2) {
return;
}
const auto [ pr, pc ] = find({ r1, c1 });
const auto [ cr, cc ] = find({ r2, c2 });
table[pr][pc] = table[pr][pc] == "" ? table[cr][cc] : table[pr][pc];
parent[cr][cc] = { pr, pc };
};
Handler unmerge = [&] (const Args &args) {
const size_t r = std::stoul(args[0]);
const size_t c = std::stoul(args[1]);
const auto [ pr, pc ] = find({ r, c });
const std::string value = table[pr][pc];
std::vector<Coordinate> to_unmerge;
forEach([&] (size_t i, size_t j) {
if (find({ r, c }) == find({ i, j })) {
to_unmerge.push_back({ i, j });
}
});
for (const auto [ i, j ]: to_unmerge) {
parent[i][j] = { i, j };
table[i][j] = "";
}
table[r][c] = value;
};
Handler print = [&] (const Args &args) {
const size_t r = std::stoul(args[0]);
const size_t c = std::stoul(args[1]);
const auto [ pr, pc ] = find({ r, c });
answer.push_back(table[pr][pc] == "" ? "EMPTY" : table[pr][pc]);
};
std::unordered_map<std::string, Handler> handlers = {
{ "UPDATE", update },
{ "MERGE", merge },
{ "UNMERGE", unmerge },
{ "PRINT", print },
};
for (const auto &command: commands) {
const auto &splited = split(command);
handlers[splited[0]](Args(std::next(splited.begin()), splited.end()));
}
return answer;
}