백준 1406. 에디터
문서 편집기를 구현하는 문제
vim이 이런식으로 구현되지 않았을까 싶다
1. 간단 설명
최대 600,000글자 까지 기록할 수 있는 에디터를 구현한다
커서를 이동하여 해당 위치에서 문자를 삭제하거나 추가할 수 있음
2. 문제 분석
1. 배열과 int cursor
를 이용하는 방법
1
2
3
4
5
6
7
8
9
a
600000
L
P a
L
P a
L
P a
...
최대 600,000글자이므로 다음 같은 예시가 주어진다면
문자 하나를 맨 앞에 넣고, 모든 원소들을 뒤로 미루어야하기 때문에
최악의 경우 250000!의 시간 복잡도가 발생함
실패!
2. 연결리스트와 반복자를 이용한 구현
- 추가, 삭제할 때 엄청난 지연이 발생하는 것을 확인했으니, 이를 보완하기 위해 연결리스트 자료구조 사용
- STL List를 이용해 구현하고, cursor를 iterator를 이용해 구현
알고리즘
1
2
3
4
5
6
7
8
1. 문자열 S를 입력받아, 이를 char형 리스트에 넣는다
2. 반복자 커서를 생성 후, 마지막 위치로 할당한다. (명령어가 수행되기 전에 커서는 문장의 맨 뒤에 위치)
3. M을 입력받고, M개의 명령어들을 입력받는다
3-1. L인 경우, 이동할 수 있다면 반복자를 앞으로 이동
3-2. D인 경우, 이동할 수 있다면 반복자를 앞으로 뒤로 이동
3-3. B인 경우, 커서의 왼쪽의 문자를 삭제해야하므로, 반복자를 앞으로 이동 후 삭제한다. 이후 뒤의 원소와 연결해준다
3-4. P인 경우, 현재 반복자의 위치에 원소를 추가한다
4. 명령을 모두 수행했다면, 연결리스트의 원소를 처음부터 끝까지 출력하고 종료
소스코드
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <list>
using namespace std;
int main() {
freopen_s(new FILE*, "input.txt", "r", stdin);
int M;
string s = "";
cin >> s;
list<char> li(s.begin(), s.end()); // 이렇게 생성 가능
list<char>::iterator cursor;
cursor = li.end();
cin >> M;
while (M--) {
char cmd, c; cin >> cmd;
switch (cmd)
{
case 'L':
if (cursor != li.begin())
cursor--;
break;
case 'D':
if (cursor != li.end())
cursor++;
break;
case 'B':
if (cursor != li.begin()) {
cursor--;
cursor = li.erase(cursor); // 삭제 하고 iterator를 반환하는듯함
}
break;
case 'P':
cin >> c;
li.insert(cursor, c); // iter 위치에 삽입
break;
}
}
for (cursor = li.begin(); cursor != li.end(); cursor++) {
cout << *cursor;
}
return 0;
}
- list 생성자로 begin, end를 사용하는법 학습
- std::list::erase()는 삭제한 원소의 다음 원소를 가리키는 이터레이터를 리턴해준다는 것을 학습
3. 스택을 이용한 구현
- 두개의 스택을 운용함
- 따로 커서는 존재하지 않음
- 가상의 커서를 기준으로 왼쪽의 내용을 저장하는 스택과, 오른쪽의 내용을 저장하는 스택
- 책의 책장을 넘기는 것과 유사하게 생각하면 쉬움
- 오른쪽 내용의 스택은 거꾸로 쌓이는것에 주의할 것
알고리즘
1
2
3
4
5
6
7
8
9
10
11
1. 왼쪽 문자들을 저장할 스택과, 오른쪽 문자들을 저장하는 스택을 생성
2. 문자열 S를 입력받아, 왼쪽 스택에 모두 집어넣는다(커서가 문장 맨 뒤에 위치하므로)
3. M을 입력받고, M개의 명령어들을 입력받는다
3-1. L인 경우, 왼쪽의 스택의 TOP을 빼서, 오른쪽 스택에 넣는다
3-2. D인 경우, 오른쪽의 스택의 TOP을 빼서, 왼쪽 스택에 넣는다
3-3. B인 경우, 왼쪽 스택에서 POP 한다
3-4. P인 경우, 왼쪽 스택에 PUSH 한다
4. 명령을 모두 수행했다면, 순서대로 출력해야하므로, 왼쪽 스택의 내용을 오른쪽 스택에 모두 집어넣는다
- 현재 모든 원소가 역순임
5. 이후 오른쪽 스택의 내용을 TOP부터 모두 출력한다
소스코드
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <stack>
using namespace std;
int main() {
freopen_s(new FILE*, "input.txt", "r", stdin);
string str; cin >> str;
stack<char> left;
stack<char> right;
for (char ch : str) {
left.push(ch);
}
int M; cin >> M;
while (M--) {
char cmd, c; cin >> cmd;
switch (cmd)
{
case 'L':
if (!left.empty()) {
right.push(left.top());
left.pop();
}
break;
case 'D':
if (!right.empty()) {
left.push(right.top());
right.pop();
}
break;
case 'B':
if (!left.empty()) {
left.pop();
}
break;
case 'P':
cin >> c;
left.push(c);
break;
default:
break;
}
}
while (!left.empty()) {
right.push(left.top());
left.pop();
}
while (!right.empty()) {
cout << right.top();
right.pop();
}
return 0;
}
- 이런 아이디어는 어떻게 떠올리는지 참 신기하다
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.