어제 밤에 조금 난이도 있는 문제를 풀어보고자 도전한 문제는 프로그래머스 표 편집 이다. 카카오 인턴십 기출 문제인데 level3 이고 카카오 답게 설명이 매우 길고 문제 조건을 파악하는 데에 시간이 조금 걸렸다.
처음 표의 행 개수를 나타내는 정수 n, 처음에 선택된 행의 위치를 나타내는 정수 k, 수행한 명령어들이 담긴 문자열 배열 cmd가 매개변수로 주어질 때, 모든 명령어를 수행한 후 표의 상태와 처음 주어진 표의 상태를 비교하여 삭제되지 않은 행은 O, 삭제된 행은 X로 표시하여 문자열 형태로 return 하도록 solution 함수를 완성해주세요.
정확성 테스트 케이스 제한 사항
효율성 테스트 케이스 제한 사항
효율성 테스트가 있는 문제이고 n 값이나 명령어 D, U 가 주어질 때 이동 값이 크기 때문에 단순한 일차원 배열이나 벡터를 사용해서는 통과할 수 없을 것 같았다.
또 중간에 위치한 데이터를 삭제하고 다시 넣는 과정이 있기 때문에 더더욱 단순 배열이나 벡터는 적합하지 않다.
중간에 데이터를 넣고 빼야하는 것을 생각해 나는 링크드리스트를 떠올렸다. 자바에는 적절한 자료형이 있는 것 같던데 c++ 에도 있나 찾아보니 <list>
자료형이 존재했고 리스트를 사용해서 코드를 작성하였다.
중간에 데이터를 넣고 빼는 코드가 필요한 것은 명령어 C
, Z
를 입력했을때를 위함인데 특히나 Z
입력시에는 삭제되었던 데이터들을 역순으로 복원 시키는 작업이 필요했다. 처음엔 Z
명령어가 연속으로 입력될 수 있는지를 몰라서 썼던 코드를 지우고 <stack>
자료구조를 사용해서 작성하였다.
하지만 리스트를 사용해서는 효율성 테스트를 통과하지 못했다. 아무래도 D
, U
명령어 수행시 표에서 가리키고 있는 위치를 이동 시키는게 선형적으로 진행될 수 밖에 없기 때문인 것 같았다. 입력되는 이동 값이 n
이라면 무조건 n
칸을 이동해야 하기 때문이다.
이 과정의 시간 복잡도를 줄이는 방법은 보통 log n
의 이동 시간을 보장하는 것이 유력하다. 찾아보니 <set>
을 사용한 경우가 있었다. <set>
자료구조는 균형 이진트리 형태로 구성되어 있기 때문에 데이터를 넣으면 알아서 정렬되고 데이터를 찾아 가는 과정도 log n
의 시간 복잡도를 보장하기 때문에 효율성 테스트를 만족할 수 있었다.
<set>
을 사용해서 다시 코드를 수정했고 참고한 포스팅에서 <set>
의 원소를 가리키는 포인터 <set>::iterator
를 앞뒤로 움직이는 함수 prev
와 next
를 사용하는 방법도 알 수 있었다.
여러모로 배운 내용이 많은 문제였다...!
#include <iostream>
#include <vector>
#include <set>
#include <stack>
// 프로그래머스 표 편집, level 3
using namespace std;
string solution(int n, int k, vector<string> cmd) {
string answer(n, 'X');
set<int> table; // 균형 이진트리 기반
stack<int> deleted;
for (int i = 0; i < n; ++i) table.insert(i);
auto iter = table.find(k);
for (int i = 0; i < cmd.size(); ++i) {
char command = cmd[i][0];
if (command=='D'){ // 아래로
int down = stoi(cmd[i].substr(2,cmd[i].size()-2));
while (down>0){
iter = next(iter);
down--;
}
}else if (command=='U'){ // 위로
int up = stoi(cmd[i].substr(2,cmd[i].size()-2));
while (up>0){
iter = prev(iter);
up--;
}
}else if (command=='C'){ // 삭제
//현재 포인터가 가리키는 데이터 삭제, 삭제된 데이터 스택에 push
deleted.push(*iter);
iter = table.erase(iter);
// erase -> 삭제된 데이터 이후의 포인터를 가리킴
// 제일 끝 데이터를 삭제 했다면 마지막 데이터를 가리키도록
if (iter==table.end()) iter = prev(iter);
}else{ // 복구
table.insert(deleted.top());
deleted.pop();
}
}
for(int i : table) answer[i] = 'O'; // 현재 set 에 남아있는 자료만 체크
return answer;
}