[JS] 백준 16113 시그널 자바스크립트 node.js 풀이
문제
zxcvber는 외계인을 연구하는 과학자다. 그는 지난 10년간 우주에서 오는 시그널를 연구했지만, 아무런 성과가 없었다. 그러던 어느 날, 갑자기 우주에서 이상한 시그널이 오기 시작했다. zxcvber는 매우 기뻐하며 시그널을 받아서 분석해보았다. 시그널은 0과 1로 이루어져 있는데, 여기서는 편의상 0을 ".", 1을 "#"으로 표시한다. 시그널은 다음과 같았다.
###.....###.#..####.#.......#.#....####.....###.#....##.#.......#.#....####.....###.#....#
다른 여러 시그널들을 분석해본 결과, zxcvber는 시그널의 길이가 항상 5의 배수라는 것을 알게 되었다. 시그널을 다섯 개로 쪼개면 뭔가 규칙이 보이지 않을까 생각한 zxcvber는 시그널을 같은 길이의 5개의 시그널로 쪼갰다. 그러자 놀라운 일이 벌어졌다. 아래는 시그널을 쪼갠 뒤 "#"을 검은색, "."을 흰색으로 표시한 그림이다.

시그널은 디지털 숫자를 나타내고 있었다! 1-3열에 8, 9-11열에 3, 13열에 1, 그리고 16-18열에 7이 나타난 것이다. 이 숫자들이 특별한 의미를 갖고 있을 것이라 생각한 zxcvber는 다른 시그널들도 해독을 하기 시작했다. 하지만 시그널들의 길이가 너무 길어서, 일일히 손으로 확인하기에는 한계가 있었다. 다만, 짧은 시그널들을 분석하면서 zxcvber는 시그널의 규칙들을 파악할 수 있었다.
1. 시그널은 "."과 "#"으로 이루어져 있다.
2. 시그널을 해독한 결과에는 반드시 숫자가 1개 이상 있다.
3. 시그널에 등장하는 모든 "#"은 올바른 숫자 패턴에 포함되어 있다.
4. 숫자와 숫자 사이에는 1열 이상의 공백이 있다. 여기서 공백은, 열의 성분이 모두 "."인 열을 의미한다.
5. 0부터 9는 아래와 같이 나타난다. 역시 "#"을 검은색, "."을 흰색으로 표시했다.

주의할 점은, 1은 다른 숫자들과는 다르게 1열을 차지한다는 것이다. zxcvber를 도와 시그널을 해독해보자!
입력
입력의 첫 줄에는 시그널의 길이 N(5 ≤ N ≤ 100,000, N은 5의 배수)이 주어진다.
다음 줄에 시그널이 주어진다. zxcvber가 찾아낸 규칙을 따르는 시그널만이 입력으로 주어진다.
출력
첫 번째 줄에 시그널을 해독하여 나오는 숫자들을 순서대로 출력한다.
풀이
해당 문제는 구현, 문자열 문제로 분류되어있다.
문자열은 무조건 5행이다. 객체 구조로 해당 숫자에 대한 3*5구조의 문자열을 담았다.
1은 저 구조에 아예 없어도 된다.
- while문을 활용해서 N/5-1 까지만 돈다. (열만 확인)
- i가 #인 부분만 확인한다. 문자열 맨 처음은 무조건 #이므로 예외없이 모두 적용된다.
- 문자열은 무조건 3열 5행이므로, 인덱스를 N/5칸씩 건너뛴다. ex) 0,1,2 -> 8,9,10 -> 16,17,18...
- 그리고 완성된 하나의 문자code와 일치하는 Obj의 키값을 찾아서 결과에 추가한다.
const filePath = process.platform === 'linux' ?
'/dev/stdin' : './input.txt';
let input = require('fs').readFileSync(filePath).toString().trim().split('\n');
const N = Number(input[0]);
const code = input[1];
const signal = {0:'####.##.##.####',1:'#####',2:'###..#####..###',3:'###..####..####',
4:'#.##.####..#..#',5:'####..###..####',6:'####..####.####',7:'###..#..#..#..#',
8:'####.#####.####',9:'####.####..####'}
let i = 0;
let result = '';
// 열은 N/5 -1 가 끝.
while(i<N/5){
// '#'인 경우
if(code[i]==='#'){
// 예외처리 : 3칸씩 검증해야하는데, 1이 맨 끝이나 그 앞의 열에 있을 경우.
if(i+1>=N/5 || i+2 >= N/5){
// 검은색으로 시작했는데 3열을 못차지한다면 Obj를 살펴볼 것도 없이 1이다.
result += '1';
break //끝부분이라서 바로 탈출하면 됨
}else{
let [j,k,l] = [i, i+1, i+2]; //3열씩 검증
let tempNum = code[j]+code[k]+code[l];
for(let m=0; m<4; m++){ // 4번 칸 건너뛰기
j += N/5
k += N/5
l += N/5
tempNum += code[j]+code[k]+code[l];
}
if(Object.values(signal).includes(tempNum)){ //해당 value를 가진 key 찾기
const findNum = Object.keys(signal).find(key=> signal[key]===tempNum);
result += findNum.toString();
i += 4 // 3칸이 아니라 4칸 건너뛰는 이유는 숫자 중간에 무조건 빈 칸이 있기때문
}else{ //만약 객체에 없는데 검은색으로 시작하면 더 볼것도 없이 무조건 1
result += '1';
i += 2 // 위 주석과 이유 동일
}
}
}else{ // #으로 시작하지 않으면(빈칸) 한칸 이동
i++
}
}
console.log(result)
어려웠던 점 :
푸는 시간은 2시간정도 걸렸다. 방법은 알겠는데 디버깅하는데 시간이 무척 오래걸렸다.
디버깅 시점이나 문제 파악을 더 빠르게 해야겠다.
1. 예외처리 부족 :
- 1이 끝(or끝의 앞)열에 있는 경우 : 문자열을 3칸씩 검증하기 위해서 while문의 조건을 while( i < (N/5) - 2)로 설정해서 오류가 났다.1은 단 1열만으로도 완성이 되기 때문에, 맨 끝열이나 끝열 앞이었다면 해당 부분을 검증하지 못한다. 여기서 시간 엄청 오래 걸렸다.
- 처음에 예외처리 조건문을
-
if(!code[i+1] || !code[i+2])이렇게 뒀다. 그림을 보면서 이해해서 자꾸 이차원 배열로 이해하다 보니, 범위를 잘못 생각해서 작성한 코드이다.
- 만약 지금 i가 검은 칸이 아니고 흰 칸일때 +1로 옮기는 조건을 생각하지 못해서 무한 루프문을 돌았다.
2. 반복문 조건 확인 부족 :
- for(let m=0; m<4; m++) 부분을 5번 반복해야 한다고 m<5로 했다.
- while(i < (N/5) -3)을 해서 2열을 빼고 계산한 게 아니라 3열을 빼고 계산했다. 여기서 시간 엄청 오래 걸렸다.
다른 풀이
1. 한 열을 기준으로 각 숫자에 따라서 조건문을 다르게 처리하는 풀이도 보았다. 이렇게 풀이할 경우 분기와 조건문이 엄청 많아진다는 단점이 있는데, 1열씩 기준으로 하다보니 머리가 아플 일은 없어 보인다.
0의 경우 예를 들면 '######...######'가 된다. (나는 행을 기준으로 풀었다)
이차원 배열을 만들어서 열을 기준으로 한칸씩 이동해서 temp 배열에 담아주고, 이게 code와 동일한지 확인한다. 만약 동일하면 temp를 다시 비운다.
단, 1의 경우 중복되는 부분이 있으니 따로 분기처리를 더 해준다.
2. 한 열씩 나아가면서 분기처리를 해주는 방법도 있었다.
(예를 들어 0의 경우 #####)
0,1,6,8의 경우 모두 #####으로 시작하기 때문에, 다음 칸을 비교하면서 추가 분기처리를 해야한다.