#1406 에디터
난이도 : 실버 3
유형 : 자료구조/ 스택
▸ 문제
한 줄로 된 간단한 에디터를 구현하려고 한다. 이 편집기는 영어 소문자만을 기록할 수 있는 편집기로, 최대 600,000글자까지 입력할 수 있다.
이 편집기에는 '커서'라는 것이 있는데, 커서는 문장의 맨 앞(첫 번째 문자의 왼쪽), 문장의 맨 뒤(마지막 문자의 오른쪽), 또는 문장 중간 임의의 곳(모든 연속된 두 문자 사이)에 위치할 수 있다. 즉 길이가 L인 문자열이 현재 편집기에 입력되어 있으면, 커서가 위치할 수 있는 곳은 L+1가지 경우가 있다.
이 편집기가 지원하는 명령어는 다음과 같다.
L | 커서를 왼쪽으로 한 칸 옮김 (커서가 문장의 맨 앞이면 무시됨) |
D | 커서를 오른쪽으로 한 칸 옮김 (커서가 문장의 맨 뒤이면 무시됨) |
B | 커서 왼쪽에 있는 문자를 삭제함 (커서가 문장의 맨 앞이면 무시됨) 삭제로 인해 커서는 한 칸 왼쪽으로 이동한 것처럼 나타나지만, 실제로 커서의 오른쪽에 있던 문자는 그대로임 |
P $ | $라는 문자를 커서 왼쪽에 추가함 |
초기에 편집기에 입력되어 있는 문자열이 주어지고, 그 이후 입력한 명령어가 차례로 주어졌을 때, 모든 명령어를 수행하고 난 후 편집기에 입력되어 있는 문자열을 구하는 프로그램을 작성하시오. 단, 명령어가 수행되기 전에 커서는 문장의 맨 뒤에 위치하고 있다고 한다.
▸ 입력
첫째 줄에는 초기에 편집기에 입력되어 있는 문자열이 주어진다. 이 문자열은 길이가 N이고, 영어 소문자로만 이루어져 있으며, 길이는 100,000을 넘지 않는다. 둘째 줄에는 입력할 명령어의 개수를 나타내는 정수 M(1 ≤ M ≤ 500,000)이 주어진다. 셋째 줄부터 M개의 줄에 걸쳐 입력할 명령어가 순서대로 주어진다. 명령어는 위의 네 가지 중 하나의 형태로만 주어진다.
▸ 출력
첫째 줄에 모든 명령어를 수행하고 난 후 편집기에 입력되어 있는 문자열을 출력한다.
문제 풀이
문자열 최대 길이는 10만, 최대 명령어 개수는 50만이므로, StringBuilder나 LinkedList의 삽입 연산은 안쓰는 것이 좋다. 내부적으로 선형탐색을 또 하기 때문에 시간초과가 발생할 수 있다.
구상
그래서 LIFO구조를 가진 Stack이라는 자료구조를 이용하여 풀이를 할 것이다.
- s : 초기에 편집기에 입력되어 있는 문자열을 저장하는 stack
- dS: 커서 움직임이나 삭제 명령으로 인해 s에서 사라진 문자를 잠시 보관해두는 stack
각 연산에 따른 stack의 동작 과정은 다음과 같다.
- 'L' : s에 가장 마지막에 있는 문자를 잠시 dS로 이동시킨다. (커서 왼쪽 이동) dS.push(s.pop());
- 'D': dS에 있는 'L'에서 집어넣었던 문자를 다시 s로 이동시킨다. (커서 오른쪽 이동) s.push(dS.pop());
- 'B' : s에 가장 마지막에 있는 문자열을 삭제한다. s.pop();
- 'P $' : 현재 커서의 상태는 dS와 s의 관계를 통해 이뤄지고 있기 때문에 바로 s에 $ 문자열을 저장해준다. s.push(op.charAt(2));
시뮬레이션
예제 1번을 시뮬레이션 돌려보면 stack의 상태는 다음과 같이 동작하는 것을 볼 수 있다.
풀이 코드
import java.io.*;
import java.util.Stack;
public class Main {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = br.readLine();
Stack<Character> s = new Stack<>();
for(int i=0; i<line.length(); i++) {
s.push(line.charAt(i));
}
int m = Integer.parseInt(br.readLine());
Stack<Character> dS= new Stack<>();
for(int i=0; i<m; i++) {
String op = br.readLine();
char o = op.charAt(0);
if(o == 'L') {
if(!s.isEmpty()) {
dS.push(s.pop());
}
}else if(o == 'D') {
if(!dS.isEmpty()) {
s.push(dS.pop());
}
}else if(o == 'B') {
if(!s.isEmpty()) {
s.pop();
}
}else {
s.push(op.charAt(2));
}
}
while(!dS.isEmpty()) {
s.push(dS.pop());
}
for(int i=0; i<s.size(); i++) {
bw.write(s.get(i));
}
bw.flush();
bw.close();
}
}
연결리스트 직접 구현 풀이 코드
그 외에도 연결리스트를 직접 구현해서 풀이하는 방법이 있다. 커서가 맨 앞에 위치해있을 때만 고려해서 메소드를 작성해주면 된다.
연결리스트 동작과정
주어진 입력값으로 초기상태를 설정해준다. 그리고 삽입(insert)메소드와 삭제(delete)메소드를 만들어주면 된다.
import java.io.*;
import java.util.Stack;
public class Main {
static class Node{
char data;
Node prev, next;
public Node() {
/* no-op */
}
public Node(char data) {
this.data = data;
this.prev = prev;
this.next = next;
}
public Node delete() {
if(this.data == '0') {
return this;
}
else {
prev.next = next;
next.prev = prev;
return this.prev;
}
}
public Node insert(char data) {
Node addNode = new Node(data);
if(this.data=='0') { // 커서 위치 맨 처음
addNode.prev = this;
addNode.next = this.next;
next.prev= addNode;
this.next = addNode;
return this.next;
}
else {
addNode.next = this.next;
addNode.prev = this;
next.prev = addNode;
this.next = addNode;
return this.next;
}
}
}
static Node init(String initLine) {
Node initNode = new Node('0');
Node prevNode = initNode;
Node curNode = null;
int size = initLine.length();
for(int i=0; i<size; i++) {
char a = initLine.charAt(i);
curNode = new Node(a);
prevNode.next = curNode;
curNode.prev = prevNode;
prevNode = curNode;
}
Node endNode = new Node('1');
curNode.next = endNode;
return initNode.next;
}
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = br.readLine();
Node cursor = init(line);
for(int i=0; i<line.length()-1; i++) {
cursor = cursor.next;
}
int m = Integer.parseInt(br.readLine());
int len= line.length();
for(int i=0; i<m; i++) {
String op = br.readLine();
char o = op.charAt(0);
if(o == 'L') {
if(cursor.data!='0') {
cursor = cursor.prev;
}
}else if(o == 'D') {
if(cursor.next.data=='1') continue;
cursor = cursor.next;
}else if(o == 'B') {
if(len>0) {
len--;
cursor = cursor.delete();
}
}else {
char add = op.charAt(2);
len++;
cursor = cursor.insert(add);
}
}
while(true) {
if(cursor.data=='0') break;
cursor = cursor.prev;
}
while(true) {
bw.write(cursor.next.data);
cursor = cursor.next;
if(cursor.next.data =='1') break;
}
bw.flush();
bw.close();
}
}
'Dot Algo∙ DS > PS' 카테고리의 다른 글
[BOJ] 백준 11437번 LCA (Java) (0) | 2021.08.01 |
---|---|
[BOJ] 백준 13209번 검역소 (Java) (0) | 2021.07.31 |
[BOJ] 백준 5397번 키로거 (Java) (1) | 2021.07.29 |
[BOJ] 백준 2579번 계단 오르기 (Java) (0) | 2021.07.28 |
[BOJ] 백준 1302번 베스트셀러 (Java) (0) | 2021.07.27 |