[JavaScript] 프로그래머스 비밀지도 / padStart(), Bitwise Or(`|`)

Gaeun·2022년 11월 23일

프로그래머스 Lv.1

목록 보기
7/11

비밀지도

문제 설명
네오는 평소 프로도가 비상금을 숨겨놓는 장소를 알려줄 비밀지도를 손에 넣었다. 그런데 이 비밀지도는 숫자로 암호화되어 있어 위치를 확인하기 위해서는 암호를 해독해야 한다. 다행히 지도 암호를 해독할 방법을 적어놓은 메모도 함께 발견했다.

1. 지도는 한 변의 길이가 n인 정사각형 배열 형태로, 각 칸은 "공백"(" ") 또는 "벽"("#") 두 종류로 이루어져 있다.
2. 전체 지도는 두 장의 지도를 겹쳐서 얻을 수 있다. 각각 "지도 1"과 "지도 2"라고 하자. 지도 1 또는 지도 2 중 어느 하나라도 벽인 부분은 전체 지도에서도 벽이다. 지도 1과 지도 2에서 모두 공백인 부분은 전체 지도에서도 공백이다.
3. "지도 1"과 "지도 2"는 각각 정수 배열로 암호화되어 있다.
4. 암호화된 배열은 지도의 각 가로줄에서 벽 부분을 1, 공백 부분을 0으로 부호화했을 때 얻어지는 이진수에 해당하는 값의 배열이다.

네오가 프로도의 비상금을 손에 넣을 수 있도록, 비밀지도의 암호를 해독하는 작업을 도와줄 프로그램을 작성하라.

나의 풀이

function solution(n, arr1, arr2) {
  // 2진수를 담은 배열 만들기
  const arr1_2 = arr1.map((x) => x.toString(2));
  const arr2_2 = arr2.map((x) => x.toString(2));

  // 0을 n만큼의 길이로 반복한 뒤, n개를 넣은 배열 만들기
  const n0 = Array(n).fill("0".repeat(n), 0, n);

  // 2진수로 바꾼 숫자의 길이 n으로 만들기 (ex: 1 -> 00001)
  // 배열의 모든 원소의 앞에 n만큼 0을 더한 뒤, 뒤에서부터 n만큼 잘라내면 됨
  for (let i in n0) {
    arr1_2[i] = (n0[i] + arr1_2[i]).slice(-n);
    arr2_2[i] = (n0[i] + arr2_2[i]).slice(-n);
  }

  // arr1과 arr2 더하기
  const add = [];

  for (let i in arr1_2) {
    add.push(Number(arr1_2[i]) + Number(arr2_2[i]));
  }

  // 0은 공백으로, 1 혹은 2는 #으로 대체하여 반환하기
  const result = [];

  for (let i in add) {
    result.push(
      add[i]
        .toString()
        .replaceAll(/[1-2]/g, "#")
        .replaceAll("0", " ")
        .padStart(n, " ")
    );
  }

  return result;
}

접근 방법

문제를 처음 읽었을 때에는 괜히 겁먹어서 뒤로가기를 눌렀었다. 그래도, 내가 공부해온 것들을 이용해서 풀어보자는 마음으로 코드를 작성하기 시작했고, 마지막까지 큰 힘을 들이지 않고 할 수 있었다. 아래의 순서로 문제를 풀기 위해 코드를 작성했다.

  1. 일단 배열 안의 숫자를 2진수로 바꾸어주었다. toString(n)의 메서드를 사용하는 경우 숫자 타입이 아닌 문자열 타입으로 반환되는데, 이를 숫자로 다시 바꾸어줄까 고민했는데, 숫자의 길이가 n 만큼을 가지고 있어야하는 다음 스텝을 위하여 바꾸지 않았다.
  2. "0"을 n의 길이만큼 가지고 있는 n개 인덱스의 배열을 만들었다.
  3. 2번에서 만든 배열을 arr1, arr2 요소 앞에 붙인 뒤, 뒤에서 n만큼 잘라내었다. 문제 설명에 나와있는 숫자로 예를 들자면, 9의 2진수는 1001인데 이를 01001로 만드는 작업이다.
  4. arr1_2와 arr2_2(기존 배열을 이진수로 만들고 길이를 n으로 만든 새 배열)을 같은 인덱스끼리 더해주었다.
  5. 0은 공백으로, 1 혹은 2는 #으로 대체하여 반환하였다. 여기에서 애를 좀 먹었는데, " #### "로 반환되어야 하는 것이 맨 앞 공백이 제거되어 "#### "로 반환되었기 때문이다. 맨 앞의 공백이 온전하게 나오기 위해 어떤 방법을 사용해야하나 구글링하다가 padStart()라는 메서드를 찾아 활용하였다.

padStart()

  • 현재 문자열의 시작을 다른 문자열로 채워, 주어진 길이를 만족하는 새로운 문자열을 반환합니다. 채워넣기는 대상 문자열의 시작(좌측)부터 적용된다.
// syntax
str.padStart(targetLength [, padString])

// 예제
const str1 = '5';

console.log(str1.padStart(2, '0'));	// expected output: "05"

const fullNumber = '2034399002125581';
const last4Digits = fullNumber.slice(-4);
const maskedNumber = last4Digits.padStart(fullNumber.length, '*');

console.log(maskedNumber);	// expected output: "************5581"

다른 사람의 풀이

function solution(n, arr1, arr2) {
    return arr1.map((v, i) => addZero(n, (v | arr2[i]).toString(2)).replace(/1|0/g, a => +a ? '#' : ' '));
}

const addZero = (n, s) => {
    return '0'.repeat(n - s.length) + s;
}
  • Single verticle bar라는 것을 이 풀이 댓글에서 보게 되었다. 이게 무엇인지 검색해보니 MDN에서 Bitwise Or(|)을 알게 되었다.
const a = 5;        // 00000000000000000000000000000101
const b = 3;        // 00000000000000000000000000000011

console.log(a | b); // 00000000000000000000000000000111
// expected output: 7

console.log((a|b).toString(2)) // 111

내가 쌩고생하며 풀었던 문제가 이렇게 쉽게 풀릴 수 있는 방법이 있었다니...🥹

다른 사람의 풀이에서 addZero라는 함수를 만들었는데 이는 내가 사용한 padStart()메서드를 활용한다면 더욱 간결하게 코드를 작성할 수 있을 것이라 생각했다. 그래서 나는 이런 식으로 다시 작성해보았다.

// 내가 다시 작성한 코드
function solution(n, arr1, arr2) {
  return arr1.map((v, i) =>
    (n, (v | arr2[i]).toString(2))
      .replace(/1|0/g, (a) => (+a ? "#" : " "))
      .padStart(n, " ")
  );
}

오늘의 교훈

  • 문자열의 첫 공백이 그대로 반환되어야 하는데 제거되는 경우, padStart() 메서드를 활용하여 원하는 길이만큼 공백을 만들어 반환할 수 있다.
  • padStart() 메서드에 대해 알게 된 뒤, 전에 풀었던 문제 중 프로그래머스 핸드폰 번호 가리기 문제에서 이 메서드를 활용해서 풀었을 수 있었겠다는 생각을 했다.
  • 2진수의 덧셈을 할 때에는 Bitwise Or(|)을 활용하자.
profile
🌱 새싹 개발자의 고군분투 코딩 일기

0개의 댓글