• Feed
  • Explore
  • Ranking
/
/
    맞았습니다!!

    BOJ : 백준 1406 에디터(JAVA)

    백준 1406 에디터 자바 풀이 💡
    알고리즘자료구조
    t
    t1s
    2025.04.26
    ·
    7 min read

    문제

    시간 제한 0.3초, 메모리 제한 512 MB
    한 줄로 된 간단한 에디터를 구현하려고 한다. 이 편집기는 영어 소문자만을 기록할 수 있는 편집기로, 최대 600,000글자까지 입력할 수 있다.
    이 편집기에는 '커서'라는 것이 있는데, 커서는 문장의 맨 앞(첫 번째 문자의 왼쪽), 문장의 맨 뒤(마지막 문자의 오른쪽), 또는 문장 중간 임의의 곳(모든 연속된 두 문자 사이)에 위치할 수 있다. 즉 길이가 L인 문자열이 현재 편집기에 입력되어 있으면, 커서가 위치할 수 있는 곳은 L+1가지 경우가 있다.
    이 편집기가 지원하는 명령어는 다음과 같다.
    L : 커서를 왼쪽으로 한 칸 옮김 (커서가 문장의 맨 앞이면 무시됨)
    D : 커서를 오른쪽으로 한 칸 옮김 (커서가 문장의 맨 뒤이면 무시됨)
    B : 커서 왼쪽에 있는 문자를 삭제함 (커서가 문장의 맨 앞이면 무시됨)
    삭제로 인해 커서는 한 칸 왼쪽으로 이동한 것처럼 나타나지만, 실제로 커서의 오른쪽에 있던 문자는 그대로임
    P $ : $라는 문자를 커서 왼쪽에 추가함
    초기에 편집기에 입력되어 있는 문자열이 주어지고, 그 이후 입력한 명령어가 차례로 주어졌을 때, 모든 명령어를 수행하고 난 후 편집기에 입력되어 있는 문자열을 구하는 프로그램을 작성하시오. 단, 명령어가 수행되기 전에 커서는 문장의 맨 뒤에 위치하고 있다고 한다.

    입력
    첫째 줄은 편집기에 입력되어 있는 문자열 (길이는 100,000를 넘지 않음)
    둘째 줄에는 명령어의 개수 M (1<=M<=500,000)
    이후 M개의 줄에 걸쳐 명령어가 주어짐

    출력
    명령 수행 후 편집기에 입력외어 있는 문자열 출력

    풀이

    ^가 커서로 보고, 예시를 통해 이해하자면
    abcd^
    L을 하면 abc^d가 되고
    D를 하면 abcd^
    B를 하면 abc^ (왼쪽에 있는 것을 삭제하되, 커서도 한 자리 줄어들어야 함)
    P 를하면abc^ (커서도 한 자리 늘어나야 함)
    이렇게 되는 것이다.

    처음 StringBuilder에 문자열을 집어넣고, idx에 StringBuilder의 길이를 넣는다.
    이후 M번동안 for문을 돌며
    1. L
    idx-1을 한 것이 0보다 클 때만 idx를 줄인다.
    2. D
    idx+1을 한 것이 length보다 작거나 같을 때만 idx를 늘린다.
    3. B
    커서 왼쪽에 있는 문자를 삭제하는 것이므로 --idx 자리의 문자를 삭제한다. (deleteCharAt)
    4. P
    idx의 위치에 insert 후, idx를 늘린다.

    import java.util.*;
    import java.io.*;
    
    public class Main {
    
    	public static void main(String[] args) throws IOException {
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		StringTokenizer st;
    		StringBuilder sb = new StringBuilder();
    		sb.append(br.readLine());
    		
    		int M = Integer.parseInt(br.readLine());
    		int idx = sb.length();
    		for(int i = 0; i<M; i++) {
    			st = new StringTokenizer(br.readLine());
    			
    			switch(st.nextToken()) {
    			case "L":
    				if(idx - 1 >= 0) {
    					idx--;
    				}
    				break;
    				
    			case "D":
    				if(idx + 1 <= sb.length()) {
    					idx++;
    				}
    				break;
    				
    			case "B":
    				if(idx > 0) {
    					sb.deleteCharAt(--idx);
    				}
    				break;
    				
    			case "P":
    				sb.insert(idx, st.nextToken());
    				idx++;
    				break;
    			}
    		}
    		
    		System.out.println(sb.toString());
    
    	}
    }

    문제를 풀며

    풀고 나서 다른 풀이를 찾아봤더니 보통은 ListIterator와 Stack을 많이 사용한 것 같다.. 사실 나도 위 코드를 짜면서 시간초과가 날지 안 날지 긴가민가 했었다 🥹

    LinkedList를 사용하는 방법은 시간 초과가 난다고 한다.
    LinkedList는 전 인덱스의 요소와, 다음 인덱스의 요소를 가지고 있어서 이전, 다음 요소를 통해 정보를 처리하는 데에는 용이하지만 삽입/삭제는 비효율적이다. (삽입/삭제 시 맨 앞~맨 끝에서 일일히 찾아봐야 하므로)

    1⃣ Iterator를 상속한 인터페이스인 ListIterator를 사용하면 커서를 앞 뒤로 이동시킬 수 있었다.

    ListIterator<Character> li = list.listIterator();

    을 통해서 선언할 수 있다.
    .hasPrevious(), .hasNext()를 통해 다음 요소가 있는지 확인할 수 있다.
    .previous(), .next()를 통해 이동, .add(), .remove()를 통해 추가 및 제거도 가능하다.

    2⃣ 스택을 통한 구현법은 이러하다.
    스택 두 개를 구현해놓고, 커서가 어디로 가느냐에 따라 각 스택에 character를 옮겨주는 것이다.
    abc^ 일 때
    스택 1에는 abc, 스택 2는 빈 칸으로 냅둔다.
    ab^c로 커서를 왼쪽으로 하나 옮기면,
    스택 1에는 ab, 스택 2에는 c를 넣어두면 된다.
    B일 때는 스택 1에서 pop, P일 때는 스택 1에 push하면 된다.

    그리고 보통 StringBuilder를 사용했을 때 시간 초과가 났다는 글이 많았는데.. delete()가 아닌 deleteCharAt()을 사용하여 시간 초과가 안 난 걸까? 기본적으로 .delete()와 .deleteCharAt() 둘의 차이가 클 것 같지는 않아, 완전한 정답은 아니고 뽀록으로 맞은 것 같다는 생각이 든다.. 😅







    - 컬렉션 아티클