Tesseractjh
한 걸음씩
Tesseractjh
전체 방문자
오늘
어제
  • 전체 (293)
    • IT (30)
      • JavaScript (7)
      • TypeScript (5)
      • React (5)
      • Next.js (3)
      • MongoDB (2)
      • Webpack (2)
      • HTML & CSS (1)
      • Git (0)
      • AWS (1)
      • 기타 (4)
    • 연습장 (259)
      • 백준(BOJ) 문제풀이 (185)
      • 프로그래머스 문제풀이 (61)
      • LeetCode 문제풀이 (2)
      • HackerRank 문제풀이 (7)
      • 낙서장 (3)
      • 기타 (1)
    • 프로젝트 (3)
      • 지뢰피하기 (1)
      • 키릴-라틴 문자 변환기 (1)
      • Flex & Grid (1)
    • 멋쟁이사자처럼 프론트엔드 스쿨 1기 (1)
      • 일기 & 회고록 (1)

인기 글

티스토리

hELLO · Designed By 정상우.
Tesseractjh

한 걸음씩

Node.js로 백준(BOJ) 문제 풀 때 유의할 점들
IT/JavaScript

Node.js로 백준(BOJ) 문제 풀 때 유의할 점들

2021. 4. 10. 18:17

 

백준에서 Node.js로 입력을 받는 방법은 크게 두 가지가 있다. 첫 번째는 readline 모듈을 사용하는 것이고, 두 번째는 fs 모듈을 사용하는 것이다. (이 글에서는 fs 모듈에 대해서만 다루겠다.)

 

Python으로 백준 문제를 풀 때 시간 초과가 나는 것을 막기 위해 input() 대신 sys.stdin.readline()을 쓰는 것처럼, Node.js도 readline보다 fs모듈을 사용하는 것이 더 빠르다.

 

var fs = require('fs');
var input = fs.readFileSync('/dev/stdin').toString().split(' ');
var a = parseInt(input[0]);
var b = parseInt(input[1]);
console.log(a+b);

위 코드는 백준 언어 도움말에서 제공하는 코드다.

 

FileSystem의 약자인 fs 모듈은 파일 처리를 하는 모듈로, 직접 입력 파일을 읽어와서 처리한다. 위 코드는 입력값 전체를 하나의 문자열로 만들어 split 메서드로 배열로 만들어 그 안의 요소를 가져다 쓰는 방식으로 입력을 받는다. 하지만 백준에서 제공하는 위 코드를 사용했는데도 에러가 발생하는 경우가 잦다.


지금까지 계속 런타임에러에 시달리면서 터득한 내 나름대로의 값을 입력받는 코드들을 정리해보았다.

// 1. 하나의 값을 입력받을 때
const input = require('fs').readFileSync('/dev/stdin').toString().trim();

// 2. 공백으로 구분된 한 줄의 값들을 입력받을 때
const input = require('fs').readFileSync('/dev/stdin').toString().trim().split(' ');

// 3. 여러 줄의 값들을 입력받을 때
const input = require('fs').readFileSync('/dev/stdin').toString().trim().split('\n');

// 4. 첫 번째 줄에 자연수 n을 입력받고, 그 다음줄에 공백으로 구분된 n개의 값들을 입력받을 때
const [n, ...arr] = require('fs').readFileSync('/dev/stdin').toString().trim().split(/\s/);

// 5. 첫 번째 줄에 자연수 n을 입력받고, 그 다음줄부터 n개의 줄에 걸쳐 한 줄에 하나의 값을 입력받을 때
const [n, ...arr] = require('fs').readFileSync('/dev/stdin').toString().trim().split('\n');

// 6. 하나의 값 또는 공백으로 구분된 여러 값들을 여러 줄에 걸쳐 뒤죽박죽 섞여서 입력받을 때
// ex) n 입력 - 공백으로 구분된 n개의 값 입력 - m 입력 - 여러 줄에 걸쳐 m개의 값 입력
const input = require('fs').readFileSync('/dev/stdin').toString().trim().split(/\s/);
const n = input[0];
const n_arr = input.slice(1, n+1);
const [m, ...m_arr] = input.slice(n+1);

// 2~6에서 입력받는 값들을 모두 String에서 Number로 바꾸려면 split()뒤에 .map(Number)를 추가

 

위 코드를 보고 몇 가지 의문이 들 수 있다.

 

문자열 값을 입력받는 것이라면 toString()을 안 붙여도 되지 않나요?

 

require("fs").readFileSync("/dev/stdin")의 반환값은 문자열이 아닌 Buffer 객체다. readFileSync의 인수로 인코딩을 지정해주지 않으면 Buffer 객체를 반환한다. 따라서 문자열로 바꾸어주지 않으면 예기치 못한 오류가 날 수 있다. 문자열로 바꾸기 위해서는 위의 코드처럼 toString() 메서드 또는 문자열 연결 연산을 통해 Buffer 객체를 문자열로 바꾸거나, readFileSync의 두 번째 인수로 인코딩을 지정해주면 된다.

const input = require('fs').readFileSync('/dev/stdin');
console.log(typeof input); // object

const input2 = require('fs').readFileSync('/dev/stdin').toString();
console.log(typeof input2); // string

const input3 = require('fs').readFileSync('/dev/stdin')+'';
console.log(typeof input3); // string

const input4 = require('fs').readFileSync('/dev/stdin', 'utf8');
console.log(typeof input4); // string

 

trim()은 왜 쓰는 건가요?

 

일부 입력값의 마지막에 개행문자가 포함된 경우가 종종 있다. 이런 경우 split("\n")할 경우 공백문자 하나를 더 갖는 배열을 반환한다. 이를 방지하기 위해서 trim()을 사용한다.

const text = '가\n나\n다\n';
console.log(text.split('\n')); // ['가', '나', '다', '']
console.log(text.trim().split('\n')); // ['가', '나', '다']

 

4번이나 6번을 쓰면 속도가 너무 느린 것 같아요.

 

split('/\s/')는 속도가 느리다. 처음 자바스크립트로 백준 문제를 푸시는 분들의 편의를 위해 위와 같이 작성하였다. 입력값이 엄청 크거나, 수행 시간을 단축하고 싶다면 split('/\s/')를 사용하지 말고 split('\n')로 한 줄 씩 나눈 뒤, 한 줄을 다시 한 번 더 나누는 방식으로 입력을 받아야 한다.


각 경우에 해당하는 백준 문제를 하나씩 뽑아 보았다.

 

1. 하나의 값을 입력받을 때

www.acmicpc.net/problem/10988

 

10988번: 팰린드롬인지 확인하기

첫째 줄에 단어가 주어진다. 단어의 길이는 1보다 크거나 같고, 100보다 작거나 같으며, 알파벳 소문자로만 이루어져 있다.

www.acmicpc.net

2. 공백으로 구분된 한 줄의 값들을 입력받을 때

www.acmicpc.net/problem/2609

 

2609번: 최대공약수와 최소공배수

첫째 줄에는 입력으로 주어진 두 수의 최대공약수를, 둘째 줄에는 입력으로 주어진 두 수의 최소 공배수를 출력한다.

www.acmicpc.net

3. 여러 줄의 값들을 입력받을 때

www.acmicpc.net/problem/1100

 

1100번: 하얀 칸

체스판은 8*8크기이고, 검정 칸과 하얀 칸이 번갈아가면서 색칠되어 있다. 가장 왼쪽 위칸 (0,0)은 하얀색이다. 체스판의 상태가 주어졌을 때, 하얀 칸 위에 말이 몇 개 있는지 출력하는 프로그램

www.acmicpc.net

 

4. 첫 번째 줄에 자연수 n을 입력받고, 그 다음줄에 공백으로 구분된 n개의 값들을 입력받을 때

www.acmicpc.net/problem/10871

 

10871번: X보다 작은 수

첫째 줄에 N과 X가 주어진다. (1 ≤ N, X ≤ 10,000) 둘째 줄에 수열 A를 이루는 정수 N개가 주어진다. 주어지는 정수는 모두 1보다 크거나 같고, 10,000보다 작거나 같은 정수이다.

www.acmicpc.net

이 문제는 n 말고 x도 있어서 const [n, x, ...arr]로 풀면 된다.

 

5. 첫 번째 줄에 자연수 n을 입력받고, 그 다음줄부터 n개의 줄에 걸쳐 한 줄에 하나의 값을 입력받을 때

www.acmicpc.net/problem/10773

 

10773번: 제로

첫 번째 줄에 정수 K가 주어진다. (1 ≤ K ≤ 100,000) 이후 K개의 줄에 정수가 1개씩 주어진다. 정수는 0에서 1,000,000 사이의 값을 가지며, 정수가 "0" 일 경우에는 가장 최근에 쓴 수를 지우고, 아닐 경

www.acmicpc.net

6. 하나의 값 또는 공백으로 구분된 여러 값들을 여러 줄에 걸쳐 뒤죽박죽 섞여서 입력받을 때

www.acmicpc.net/problem/2167

 

2167번: 2차원 배열의 합

첫째 줄에 배열의 크기 N, M(1 ≤ N, M ≤ 300)이 주어진다. 다음 N개의 줄에는 M개의 정수로 배열이 주어진다. 배열에 포함되어 있는 수는 절댓값이 10,000보다 작거나 같은 정수이다. 그 다음 줄에는

www.acmicpc.net

위의 코드와는 상황이 조금 다르지만 split(/\s+/)로 분리하여 적절하게 변수를 지정해주면 된다.

 


이렇게 입력에 신경을 썼음에도 런타임에러가 나거나 기타 다른 이유로도 문제가 통과되지 않는 경우가 있다.

 

1. 특정 문제에서 readFileSync가 먹히지 않는 경우

www.acmicpc.net/problem/14681

 

14681번: 사분면 고르기

점 (x, y)의 사분면 번호(1, 2, 3, 4 중 하나)를 출력한다.

www.acmicpc.net

위 문제는 readline 모듈을 사용하면 풀리지만 어떤 이유에서인지 fs모듈의 readFileSync를 사용하면 런타임에러가 발생한다.

 

최근에 백준에 이와 관련된 공지가 올라오기도 하였다.

www.acmicpc.net/board/view/66736

 

글 읽기 - node.js 런타임 에러

댓글을 작성하려면 로그인해야 합니다.

www.acmicpc.net

 

2. Node.js로는 해당 문제의 메모리제한을 넘기지 않고 풀 수 없는 경우

www.acmicpc.net/problem/10989

 

10989번: 수 정렬하기 3

첫째 줄에 수의 개수 N(1 ≤ N ≤ 10,000,000)이 주어진다. 둘째 줄부터 N개의 줄에는 숫자가 주어진다. 이 수는 10,000보다 작거나 같은 자연수이다.

www.acmicpc.net

https://www.acmicpc.net/problem/11723

 

11723번: 집합

첫째 줄에 수행해야 하는 연산의 수 M (1 ≤ M ≤ 3,000,000)이 주어진다. 둘째 줄부터 M개의 줄에 수행해야 하는 연산이 한 줄에 하나씩 주어진다.

www.acmicpc.net

https://www.acmicpc.net/problem/2293

 

2293번: 동전 1

첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다.

www.acmicpc.net

https://www.acmicpc.net/problem/10886

 

10886번: 0 = not cute / 1 = cute

준희는 자기가 팀에서 귀여움을 담당하고 있다고 생각한다. 하지만 연수가 볼 때 그 의견은 뭔가 좀 잘못된 것 같았다. 그렇기에 설문조사를 하여 준희가 귀여운지 아닌지 알아보기로 했다.

www.acmicpc.net

[10989번 수 정렬하기 3], [11723번 집합], [2293번 동전 1]

위 세 문제는 현재 Node.js로 통과한 사람이 단 한 명도 없다... (2021.09.10 확인)

 

[10886번 0 = not cute / 1 = cute]

이 문제는 현재(2022.10.14 확인) 2명이 통과한 기록이 있으나, 이는 각각 4년 전, 2년 전 기록으로 Node.js의 버전이 높아짐에 따라 현재는 통과가 되지 않는다. (3 ~ 4년전 Node.js로 푼 기록이 압도적으로 수행 시간이 짧은 이유)

 

현재 몇몇 문제에 대한 Node.js 메모리 제한 변경 논의가 이루어지고 있다.

댓글에 위 문제들 중 일부가 언급되어 있는데, 조만간 메모리 제한이 완화될 가능성이 보인다.

(2021.07.30 확인)

https://www.acmicpc.net/board/view/71957

 

글 읽기 - Swift, node.js 추가 시간/메모리 제한을 위한 소스 코드를 구합니다.

댓글을 작성하려면 로그인해야 합니다.

www.acmicpc.net

 

하지만 실제로 불가능한 것인지는 알 수 없다.

www.acmicpc.net/problem/15552

 

15552번: 빠른 A+B

첫 줄에 테스트케이스의 개수 T가 주어진다. T는 최대 1,000,000이다. 다음 T줄에는 각각 두 정수 A와 B가 주어진다. A와 B는 1 이상, 1,000 이하이다.

www.acmicpc.net

이 문제 역시 Node.js로 풀 수 없다는 말이 게시판에 올라왔었다. (www.acmicpc.net/board/view/32852)

그러나 1년 후부터 이 문제를 Node.js로 통과한 사람들이 등장하기 시작했다.


여하튼 Node.js로 백준에서 문제 풀기가 참 까다로운 것 같습니다.

혹시 틀린 내용이 있다면 지적 환영합니다!

저작자표시 비영리 (새창열림)

'IT > JavaScript' 카테고리의 다른 글

[JS] 자바스크립트로 엑셀 파일(xlsx) 만들어서 다운받기  (0) 2022.08.05
[JS] Invalid regular expression: invalid group specifier name  (0) 2022.06.21
[JS] Selection API로 선택된 텍스트 정보 가져오기  (0) 2022.06.01
[JS] 자바스크립트의 배열 생성 방법  (0) 2021.07.29
[JS] 함수의 매개변수와 RangeError  (0) 2021.07.23
    'IT/JavaScript' 카테고리의 다른 글
    • [JS] Invalid regular expression: invalid group specifier name
    • [JS] Selection API로 선택된 텍스트 정보 가져오기
    • [JS] 자바스크립트의 배열 생성 방법
    • [JS] 함수의 매개변수와 RangeError
    Tesseractjh
    Tesseractjh
    바닐라 자바스크립트를 좋아하는 개발자입니다

    티스토리툴바