본문 바로가기
알고리즘/백준

[백준/연결리스트/JS] 에디터

by 1two13 2023. 7. 6.
728x90
반응형

문제 설명


 

1406번: 에디터

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

www.acmicpc.net

  • 소문자만, 600000글자까지
  • 커서 위치: 문장 맨 앞, 문장 맨 뒤, 문자 사이
  • 맨 처음에 커서는 맨 뒤에 위치
  • L: 왼쪽으로 한칸
  • D: 오른쪽으로 한칸
  • B: 왼쪽에 있는 문자 삭제
  • P$: $를 커서 왼쪽에 추가
  • 입력값: 문자열, 명령어 개수, 명령어들
  • 출력값: 입력값에 모든 명령어 수행한 후의 결과값

 

시간 복잡도


arr.splice()

삭제하려는 요소의 위치, 배열 개수에 따라서 시간 복잡도가 달라진다. 

 

삭제하려는 요소가 배열의 맨 끝에 위치할 경우 => O(1)

그 외 => O(n)

 

arr.slice()

자르는 요소의 개수에 따라 시간 복잡도가 달라진다. 

 

자르는 요소를 k개라고 할 때 O(k)의 시간복잡도를 가진다. 

 

틀린 부분


let [str, n, ...list] = require('fs').readFileSync('/dev/stdin').toString().trim().split('\n');
list = list.map((el) => el.split(' '));
str = str.split('');

// 처음 커서 위치
let cursor = str.length;

for (let i = 0; i < Number(n); i++) {
  let el = list[i];
  // list에 따른 커서 위치 이동
  if (el[0] === 'P') {
    // str에 값 추가
    // str = [...str.slice(0, cursor), el[1], ...str.slice(cursor)];
    str.splice(cursor, 0, el[1]);
    // 커서 위치 이동
    cursor++;
  } else if (el[0] === 'L' && cursor > 0) {
    cursor--;
  } else if (el[0] === 'D' && cursor < str.length) {
    cursor++;
  } else if (el[0] === 'B' && cursor > 0) {
    // 커서 왼쪽에 있는 문자 제거
    // str = [...str.slice(0, cursor - 1), ...str.slice(cursor)];
    str.splice(cursor - 1, 1);
    cursor--;
  }
}

console.log(str.join(''));

splie와 slice 모두 시간복잡도가 오래 걸리는 메소드라 테스트 코드 모두 통과하나 시간 초과 오류가 났다. 

 

 

다른 풀이


class Node {
  constructor(value) {
    this.value = value;
    this.next = null;
    this.prev = null;
  }
}

// 예를 들어 A, B 사이에 C를 넣는다고 가정할 때, 
function insertAfter(node, value) {
  // 새로 추가할 노드 생성(C)
  const newNode = new Node(value);
  // 새로운 노드의 이전 노드는 현재 노드(A)
  newNode.prev = node;
  // 새로운 노드의 다음 노드는 현재 노드의 다음 노드(B)
  newNode.next = node.next;
  // 중간에 삽입하는 경우
  if (node.next) {
    // B.prev는 새로운 노드
    node.next.prev = newNode;
  }
  // 끝에 삽입하는 경우 새로 추가한 노드를 다음 노드로 변경
  node.next = newNode;
  // 새로 추가한 노드 리턴
  return newNode;
}

// 예를 들어 A, C, B가 연결되어 있다고 가정할 때
function deleteNode(node) {
  // C.prev는 A
  const prevNode = node.prev;
  // C.next는 B
  const nextNode = node.next;
  if (prevNode) {
    // A.next는 B
    prevNode.next = nextNode;
  }
  if (nextNode) {
    // B.prev는 A
    nextNode.prev = prevNode;
  }
}

let [str, n, ...list] = require('fs').readFileSync('/dev/stdin').toString().trim().split('\n');
list = list.map((el) => el.split(' '));

// 연결 리스트의 head에 더미 노드 할당
const head = new Node(null);
let cursor = head;

for (let i = 0; i < str.length; i++) {
  cursor = insertAfter(cursor, str[i]);
}

for (let i = 0; i < n; i++) {
  let el = list[i];
  if (el[0] === 'P') {
    cursor = insertAfter(cursor, el[1]);
  } else if (el[0] === 'L' && cursor.prev) {
    cursor = cursor.prev;
  } else if (el[0] === 'D' && cursor.next) {
    cursor = cursor.next;
  } else if (el[0] === 'B' && cursor.prev) {
    const prevNode = cursor.prev;
    deleteNode(cursor);
    cursor = prevNode;
  }
}

let result = '';
let currentNode = head.next;
while (currentNode) {
  result += currentNode.value;
  currentNode = currentNode.next;
}

console.log(result);

코드 짜는 것도 읽는 것도 쉽지 않다.. 연습해야겠다. 

연결 리스트는 class를 사용하여 구현하는 것이 더 쉽다. 

 

 

스택으로 통과한 코드 추가


let [str, m, ...input] = require('fs').readFileSync('/dev/stdin').toString().trim().split('\n');
let stack = [];
let arr = [];
input = input.map((el) => el.split(' '));

for (let i = 0; i < str.length; i++) {
  stack.push(str[i]);
}

for (let i = 0; i < Number(m); i++) {
  let el = input[i][0];
  // $ 값 stack에 추가
  if (el === 'P') {
    stack.push(input[i][1]);
  }
  // 맨 앞이면 무시, stack.pop() 값 arr에 추가
  else if (el === 'L' && stack.length > 0) {
    let popItem = stack.pop();
    arr.push(popItem);
  }
  // 맨 뒤면 무시, arr.pop() 값 stack에 추가
  else if (el === 'D' && arr.length > 0) {
    let popItem = arr.pop();
    stack.push(popItem);
  }
  // 맨 앞이면 무시, stack.pop()
  else if (el === 'B' && stack.length > 0) {
    stack.pop();
  }
}

arr.reverse();
console.log([...stack, ...arr].join(''));

 

기억해야 하는 정보


  • splice와 slice 모두 값비싼 작업이다. 
  • 따라서 위와 같은 문제에서는 연결리스트를 사용하여 코드를 작성해야 한다.

 

참고자료


 


질문이나 잘못된 점은 댓글로 남겨주세요 :)💖

728x90
반응형

댓글