들어가며

이 포스팅에서는 leetcode 추천 문제 top 75를 자바스크립트로 풀어보며 자바스크립트 공부와 알고리즘 공부를 동시에 합니다. 문제와 풀이를 적을 건데, 틀린 부분이나 더욱 개선될 부분이 있다면 댓글로 달아주시면 정말 감사하겠습니다.

번외편

이번에는 번외적으로 leetcode 추천 문제 75선이 아닌 카카오 신입 공채 문제였던 문제를 풀이해보겠습니다.

문제

다트 게임

문제 설명

문제 개요

다트1.png

다트2.png

예제

다트3.png

풀이 과정

일단 코딩테스트 문제인만큼 빠르게 푸는데 초점을 두었습니다.

문제를 간단히 추려보면, 연속된 문자열이 들어오고 그에 대한 처리를 해주라는 문제입니다.

입력되는 문자열의 패턴은 저는 3가지로 생각했습니다.

  1. 점수 숫자
  2. Single, Double, Triple을 의미하는 S, D, T
  3. 옵션을 의미하는 * 혹은 #

이렇게 패턴의 경우의 수만 정의되면 나머지 풀이는 굉장히 간단합니다.

function dartScore(str) {
  let strNum = '0';
  let accArr = [0, 0, 0];
  let accIdx = -1;

  for (let i = 0; i < str.length; i++) {
    let character = str[i];
    if (
      // Check if it is '0' to '9' number
      character.charCodeAt(0) >= '0'.charCodeAt(0) &&
      character.charCodeAt(0) <= '9'.charCodeAt(0)
    ) {
      strNum += character;
    } else if (character === 'S' || character === 'D' || character === 'T') {
      // Single, Double, Triple Area Calculation
      accIdx++;
      let multiplyOperandNum = 1;

      if (character === 'D') {
        multiplyOperandNum = 2;
      } else if (character === 'T') {
        multiplyOperandNum = 3;
      }

      accArr[accIdx] += parseInt(strNum, 10) ** multiplyOperandNum;
      strNum = '0';
    } else if (character === '*' || character === '#') {
      // Score Options
      let multiplyOperandNum = -1;
      if (character === '*') {
        multiplyOperandNum = 2;

        if (accIdx - 1 >= 0) {
          accArr[accIdx - 1] = accArr[accIdx - 1] * multiplyOperandNum;
        }
      }

      accArr[accIdx] = accArr[accIdx] * multiplyOperandNum;
    }
  }

  return accArr.reduce((acc, curV) => acc + curV);
}

console.log(dartScore('1S2D*3T'));
console.log(dartScore('1D2S#10S'));
console.log(dartScore('1D2S0T'));
console.log(dartScore('1S*2T*3S'));
console.log(dartScore('1D#2S*3S'));
console.log(dartScore('1T2D3D#'));
console.log(dartScore('1D2S3T*'));

문제를 풀다가 고민했던 몇몇가지 이슈사항과 함께 소스코드를 설명해보겠습니다.

  1. 문자열을 한개씩 처리하면 발생하는 10(두 자리 숫자)에 대한 처리

문자열 내부에서 점수에 대한 숫자를 찾아낼 때, 두가지 방법이 있습니다. 첫번째는 제가 위에 해결한 방법처럼 문자열을 한 글자씩 보면서 숫자 범위의 ASCII 코드 인지 확인하는 방법이 있습니다. 두번째는 정규표현식을 이용하는 방법이 있습니다.

저는 빠르게 풀기 위해서 숫자 ASCII 코드의 범위 내부인지를 if문으로 확인하는 방식으로 풀었습니다. 0은 ASCII 코드로 48 9는 ASCII 코드로 57입니다. 문자열을 ASCII코드로 변환했을 때 이 범위 내부에 있다면 숫자가 들어왔다는 것을 알 수 있습니다.

일단 숫자가 넘어왔는지 판단하는 로직을 작성했고, 이제 문제는 숫자가 2자리일 때입니다. 다트 게임의 점수는 0점부터 10점까지 가능하므로, 10점이 들어왔을 때는 1이 먼저 들어온 이후에 0이 들어오게 됩니다. 그래서 저는 strNum이라는 변수를 만들고 strNum += character;코드를 이용하여 여태까지 입력된 숫자를 기록하였습니다.

또한 S, D, T등의 토큰이 들어왔을 때는 숫자 입력에 대한 문자열 패턴의 범위는 끝난 것으로 보아 strNum을 초기화해주었습니다.

  1. 옵션에 대한 처리

다트 게임에는 '옵션'이라는 게 존재합니다. 문제에 기재된 옵션을 간단히 정의하자면, #문자열은 내가 얻은 점수를 -로 변환하고, *문자열은 내가 이전에 얻은 점수와 현재 얻은 점수에 대해 *2를 해주는 옵션입니다.

여기서 고려해야 할 것은 * 문자열을 받았을 때, 내가 이전에 받은 점수가 존재하지 않는다면 현재의 점수에만 2배를 곱하는 것 정도가 있을 것 같습니다.

애초에 스코어는 3번밖에 생성되지 않으므로, accArr = [0, 0, 0]이라는 배열을 미리 생성하고, 이 배열에서 연산을 처리했습니다. 그리고 * 문자열을 받았을 때, 이전에 받은 점수가 존재하지 않으면 처리하지 않아야 하므로 이전 인덱스가 0보다 작다면 현재 값에만 연산하도록 처리하였습니다.

해당 코드는 다음과 같습니다.

if (character === '*') {
  multiplyOperandNum = 2;

  if (accIdx - 1 >= 0) {
    accArr[accIdx - 1] = accArr[accIdx - 1] * multiplyOperandNum;
  }
}

총평 및 배운점

전반적으로 문제는 별달리 어려울 것 없이 평이하였습니다. 그런데 이 문제가 왜 앞의 문제였던 비밀지도보다 정답률이 낮은지는 의문입니다. 아마 특정한 인풋에 대해서 에러가 나는 경우가 있었나 싶습니다. 제 코드도 완벽하다 자부할 수는 없지만, 카카오 블로그에 기재된 테스트 케이스는 모두 통과하여 일단 올려보았습니다.

읽어주신 분들이 있다면 감사합니다.