백준 1406 C++ (에디터)

PS / / 2021. 2. 28. 14:26

커리큘럼? 대로 풀어오면서 이렇게 막힌 문제가 없었던것 같은데, 좀 고전했던 문제였다...ㅋㅋㅋㅋㅋ

제한시간의 중요성을 다시 한 번 느끼게 해준 문제였다.

시간 제한 메모리 제한 정답률
0.3초 512MB 27.204%

 

문제

한 줄로 된 간단한 에디터를 구현하려고 한다. 이 편집기는 영어 소문자만을 기록할 수 있는 편집기로, 최대 600,000글자까지 입력할 수 있다.

이 편집기에는 '커서'라는 것이 있는데, 커서는 문장의 맨 앞(첫 번째 문자의 왼쪽), 문장의 맨 뒤(마지막 문자의 오른쪽), 또는 문장 중간 임의의 곳(모든 연속된 두 문자 사이)에 위치할 수 있다. 즉 길이가 L인 문자열이 현재 편집기에 입력되어 있으면, 커서가 위치할 수 있는 곳은 L+1가지 경우가 있다.

이 편집기가 지원하는 명령어는 다음과 같다.

L 커서를 왼쪽으로 한 칸 옮김 (커서가 문장의 맨 앞이면 무시됨)
D 커서를 오른쪽으로 한 칸 옮김 (커서가 문장의 맨 뒤이면 무시됨)
B 커서 왼쪽에 있는 문자를 삭제함 (커서가 문장의 맨 앞이면 무시됨)
삭제로 인해 커서는 한 칸 왼쪽으로 이동한 것처럼 나타나지만, 실제로 커서의 오른쪽에 있던 문자는 그대로임
P $ $라는 문자를 커서 왼쪽에 추가함

초기에 편집기에 입력되어 있는 문자열이 주어지고, 그 이후 입력한 명령어가 차례로 주어졌을 때, 모든 명령어를 수행하고 난 후 편집기에 입력되어 있는 문자열을 구하는 프로그램을 작성하시오. 단, 명령어가 수행되기 전에 커서는 문장의 맨 뒤에 위치하고 있다고 한다.

입력

첫째 줄에는 초기에 편집기에 입력되어 있는 문자열이 주어진다. 이 문자열은 길이가 N이고, 영어 소문자로만 이루어져 있으며, 길이는 100,000을 넘지 않는다. 둘째 줄에는 입력할 명령어의 개수를 나타내는 정수 M(1 ≤ M ≤ 500,000)이 주어진다. 셋째 줄부터 M개의 줄에 걸쳐 입력할 명령어가 순서대로 주어진다. 명령어는 위의 네 가지 중 하나의 형태로만 주어진다.

출력

첫째 줄에 모든 명령어를 수행하고 난 후 편집기에 입력되어 있는 문자열을 출력한다.

 

핵심

  • string으로 해결하려 하지마라. string은 기본적으로 배열 기반이기 때문에 중간에 있는 요소를 삭제할 때 시간이 엄청 오래걸린다.(중간의 한 글자를 삭제하면, 그 뒤에 있는 글자들을 모두 이전항으로 옮겨야 하기 때문에 이거만으로 최대 O(n)이 걸릴 수 있다.)
  • 요소의 삭제와 수정이 빈번하게 일어난다면, list자료형을 적극 이용해보자(Doubly Linked List)

코드

#include <iostream>
#include <algorithm>
#include <string>
#include <list>
#include <iterator>


using namespace std;

int n;
list<char> str;

string original;
string command;

void print(list<char> l){
    for(list<char>::iterator it = l.begin(); it != l.end(); it++){
        cout << *it;
    }
    cout << '\n';
}


int main(int argc, char* argv[]) {
	ios_base :: sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    
    cin >> original;
    
    for(int i=0;i<original.length();i++){
        str.push_back(original[i]);
    }
    
    cin >> n;
    cin.ignore();
    
    list<char>::iterator iter = str.end();
    
    while(n--){
        getline(cin, command);
        
        if(command[0] == 'L'){
            if(iter != str.begin()) iter--;
        }
        else if(command[0] == 'D'){
            if(iter != str.end()) iter++;
        }
        else if(command[0] == 'B'){
            if(iter != str.begin()){
                iter = str.erase(--iter);
            }
        }
        else{
            str.insert(iter, command[2]);
        }
    }
    
    print(str);
    
	
	return 0;
}

해설

처음에 string으로 하려 했으나 일단 구현 자체가 빠르게 되지 않았다.

string의 erase함수를 이용해서 구현했으나...! 바로 시간제한이 걸려버렸다ㅜㅜ

생각해보니 erase 하나만으로 O(n)을 달성해 버렸다.

예를들어 3번째 있는 글자를 삭제하려면 4번째부터 마지막 글자까지 전부 한칸씩 당겨야하기 때문이다..

반복문 안에 들어가있으니 알고리즘의 시간복잡도는 최소 O(n^2)일텐데 제한시간은 고작 0.3초였다.

 

결국 구글링을 하게 되었고, 요소 수정을 자주 할 것이라면 리스트를 사용해야 한다는 것을 깨달았다!

(사실 그동안 컨테이너는 벡터만 썼었는데, 이번 문제를 풀면서 리스트를 공부할 수 있었다.)

문제대로 구현하면 크게 어려울 것은 없고, 다만 처음과 끝 부분에서만 주의해 주면 된다.

(문제에서도 처음과 끝부분에서 일어나는 연산은 무시하라고 되어있다.)

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기