문제

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

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

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

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

 

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

입력

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

출력

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

 


틀린 이유

간 초과

처음에는 string으로 문자열을 받고 string 내장 함수를 사용해서 문자를 이리 뺐다 저리 뺐다 했다. 그런데 string 내장 함수의 실행 속도가 해당 문제 요구 기준에 못 미치는 듯하다.

 

해결

list

string으로 문자열을 받되 list 컨테이너에 집어넣어서 이리 뺐다 저리 뺐다 했다. list 내장 함수는 요구 기준에 합당했나 보다.

iterator의 왼쪽? 오른쪽?

"B" 명령어는 커서(iterator)의 왼쪽 문자를 지워 주어야 한다. 그런데 계속 커서의 오른쪽 문자를 지우게끔 erase(iter); 를 하고 있었다.. iterator를 왼쪽으로 옮긴 후 (--) erase를 해 줬어야 했다! 이건 내가 바보 같아서 오래 잡고 있었던 부분이다.

 

참고 기술

end

list의 end iterator는 해당 컨테이너의 맨 끝 원소를 가리키는 게 아니다. tail 개념이다. 만약 end iterator를 출력한다면 접근 오류가 있을 것이다.

insert

list.insert(iter, val); 를 했을 때 iter는 val을 가리키지 않고 원래 가리키던 원소를 가르킨다.

erase

random access iterator인 vector iterator와는 달리 bidirection iterator인 list iterator는 erase를 했을 때 iterator가 다음 위치를 잃어버리게 된다. 따라서 erase 함수의 리턴 값을 사용하여 iterator를 갱신시켜 주어야 한다.

 

 

#include <iostream>
#include <string>
#include <list>
using namespace std;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	string sen;
	cin >> sen;

	list<char> clist;
	string::iterator siter = sen.begin();
	for (; siter != sen.end(); ++siter) {
		clist.push_back(*siter);
	}

	int sensize = sen.size();

	int ordercnt;
	cin >> ordercnt;

	string order;
	char val;

	list<char>::iterator iter = clist.end();

	// ** list iterator의 end는 리스트의 맨 끝 원소를 가리키는게 아니다. tail 개념이다. end iterator를 cout 한다면 뛟뛟뛟 하고 나올 것이다.
	// cout << *iter << endl;

	while (ordercnt > 0) {
		cin >> order;
		if (order == "P")
		{
			cin >> val;
			// ** list iterator는 insert를 하여도 iterator가 원래 가르키던 값을 가르킨다.
			clist.insert(iter, val);
			if (iter != clist.end()) {
				//iter++;
			}
		}
		else if (order == "L") {
			if(iter != clist.begin()){
				iter--;
			}
		}
		else if (order == "D") {
			if (iter != clist.end()) {
				iter++;
			}
		}
		else if (order == "B") {
			if (iter != clist.begin()) {
				// ** random access iterator인 vector iterator와는 달리, bidirection iterator인 list iterator는 해당 원소 erase 시 iterator가 다음 위치를 잃어버린다.
				// 따라서 erase 함수의 리턴 값을 사용하여 갱신시켜준다.
				// ** 이 부분 떄문에 30분 고민한듯 ㅜㅜㅜㅜ 이터레이터(커서) 왼쪽 문자를 지워줘야 하잖아!! 그러니까 이터레이터를 왼쪽으로 옮긴 후(--) erase 해 줘야지!! 바보얌 
				iter--;
				iter = clist.erase(iter);
			}
		}
		ordercnt--;
	}

	list<char>::iterator result_iter = clist.begin();

	for (; result_iter != clist.end(); result_iter++) {
		cout << *result_iter;
	}

}

 

list 컨테이너 iterator에 대해서 많이 배워 가는 시간이었다. 학부 수업 때 stl 좀 더 세세하게 배워 놓을 걸..! 이렇게 발목 잡힐 줄은 몰랐다.

'알고리즘 > BOJ' 카테고리의 다른 글

백준 1181 : 단어 정렬  (0) 2021.06.06
백준 1026 : 보물  (0) 2021.06.06
백준 10866 : 덱  (0) 2021.05.26
백준 10845 : 큐  (0) 2021.05.26
백준 10828 : 스택  (0) 2021.05.22

+ Recent posts