본문 바로가기

Dot Algo∙ DS/PS

[BOJ] 백준 1406번 에디터 (Java)

    #1406 에디터

    난이도 : 실버 3

    유형 : 자료구조/ 스택

     

    1406번: 에디터

    첫째 줄에는 초기에 편집기에 입력되어 있는 문자열이 주어진다. 이 문자열은 길이가 N이고, 영어 소문자로만 이루어져 있으며, 길이는 100,000을 넘지 않는다. 둘째 줄에는 입력할 명령어의 개수

    www.acmicpc.net

    ▸ 문제

    한 줄로 된 간단한 에디터를 구현하려고 한다. 이 편집기는 영어 소문자만을 기록할 수 있는 편집기로, 최대 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의 상태는 다음과 같이 동작하는 것을 볼 수 있다.

    예제 1번 시뮬레이션

     

    풀이 코드 

    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)메소드를 만들어주면 된다.

    init(), P x 동작 과정
    L, P y 동작과정

     

    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();
    	}
    }