DEVELOP
article thumbnail
 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

GitHub - cjy00n/coding-test

Contribute to cjy00n/coding-test development by creating an account on GitHub.

github.com


실패율 #42889 


문제 설명
실패율


슈퍼 게임 개발자 오렐리는 큰 고민에 빠졌다. 그녀가 만든 프랜즈 오천성이 대성공을 거뒀지만, 요즘 신규 사용자의 수가 급감한 것이다. 원인은 신규 사용자와 기존 사용자 사이에 스테이지 차이가 너무 큰 것이 문제였다.

이 문제를 어떻게 할까 고민 한 그녀는 동적으로 게임 시간을 늘려서 난이도를 조절하기로 했다. 역시 슈퍼 개발자라 대부분의 로직은 쉽게 구현했지만, 실패율을 구하는 부분에서 위기에 빠지고 말았다. 오렐리를 위해 실패율을 구하는 코드를 완성하라.

실패율은 다음과 같이 정의한다.
스테이지에 도달했으나 아직 클리어하지 못한 플레이어의 수 / 스테이지에 도달한 플레이어 수
전체 스테이지의 개수 N, 게임을 이용하는 사용자가 현재 멈춰있는 스테이지의 번호가 담긴 배열 stages가 매개변수로 주어질 때, 실패율이 높은 스테이지부터 내림차순으로 스테이지의 번호가 담겨있는 배열을 return 하도록 solution 함수를 완성하라.

제한사항
스테이지의 개수 N은 1 이상 500 이하의 자연수이다.
stages의 길이는 1 이상 200,000 이하이다.
stages에는 1 이상 N + 1 이하의 자연수가 담겨있다.
각 자연수는 사용자가 현재 도전 중인 스테이지의 번호를 나타낸다.
단, N + 1 은 마지막 스테이지(N 번째 스테이지) 까지 클리어 한 사용자를 나타낸다.
만약 실패율이 같은 스테이지가 있다면 작은 번호의 스테이지가 먼저 오도록 하면 된다.
스테이지에 도달한 유저가 없는 경우 해당 스테이지의 실패율은 0 으로 정의한다.


입출력 예
N stages result
5 [2, 1, 2, 6, 2, 4, 3, 3] [3,4,2,1,5]
4 [4,4,4,4,4] [4,1,2,3]

 

▼ 나의 풀이 

function solution(N, stages) {
  var answer = [];
  var s = new Array(N).fill(0);
  stages.map((v) => {
    if (v <= N) s[v - 1]++;
  });
  let count = 0;
  s.map((v, i) => {
    answer.push([i + 1, v / (stages.length - count)]);
    count += v;
  });

  return answer.sort((a, b) => b[1] - a[1]).map((v) => v[0]);
}
  • 길이가 N인 0으로 채워진 배열 s에 stages를 map을 돌면서 갯수만큼 값을 증가시킨다. (N 이상의 값은 셀 필요 없으므로 if문을 사용했다.)
  • s에 map을 돌면서 answer 배열에 배열 형태로 추가한다. [스테이지, 실패율]
  • answer을 sort를 하고 map을 돌면서 스테이지만 출력한다. 
  • 다른 사람들은 for문 안에 filter를 넣는 식으로 푸는데, 그러면 시간이 너무 오래걸린다. 시간복잡도가 O(N^2)이다. 

덧칠하기 #161989


문제 설명
어느 학교에 페인트가 칠해진 길이가 n미터인 벽이 있습니다. 벽에 동아리 · 학회 홍보나 회사 채용 공고 포스터 등을 게시하기 위해 테이프로 붙였다가 철거할 때 떼는 일이 많고 그 과정에서 페인트가 벗겨지곤 합니다. 페인트가 벗겨진 벽이 보기 흉해져 학교는 벽에 페인트를 덧칠하기로 했습니다.

넓은 벽 전체에 페인트를 새로 칠하는 대신, 구역을 나누어 일부만 페인트를 새로 칠 함으로써 예산을 아끼려 합니다. 이를 위해 벽을 1미터 길이의 구역 n개로 나누고, 각 구역에 왼쪽부터 순서대로 1번부터 n번까지 번호를 붙였습니다. 그리고 페인트를 다시 칠해야 할 구역들을 정했습니다.

벽에 페인트를 칠하는 롤러의 길이는 m미터이고, 롤러로 벽에 페인트를 한 번 칠하는 규칙은 다음과 같습니다.

롤러가 벽에서 벗어나면 안 됩니다.
구역의 일부분만 포함되도록 칠하면 안 됩니다.
즉, 롤러의 좌우측 끝을 구역의 경계선 혹은 벽의 좌우측 끝부분에 맞춘 후 롤러를 위아래로 움직이면서 벽을 칠합니다. 현재 페인트를 칠하는 구역들을 완전히 칠한 후 벽에서 롤러를 떼며, 이를 벽을 한 번 칠했다고 정의합니다.

한 구역에 페인트를 여러 번 칠해도 되고 다시 칠해야 할 구역이 아닌 곳에 페인트를 칠해도 되지만 다시 칠하기로 정한 구역은 적어도 한 번 페인트칠을 해야 합니다. 예산을 아끼기 위해 다시 칠할 구역을 정했듯 마찬가지로 롤러로 페인트칠을 하는 횟수를 최소화하려고 합니다.

정수 n, m과 다시 페인트를 칠하기로 정한 구역들의 번호가 담긴 정수 배열 section이 매개변수로 주어질 때 롤러로 페인트칠해야 하는 최소 횟수를 return 하는 solution 함수를 작성해 주세요.

제한사항
1 ≤ m ≤ n ≤ 100,000
1 ≤ section의 길이 ≤ n
1 ≤ section의 원소 ≤ n
section의 원소는 페인트를 다시 칠해야 하는 구역의 번호입니다.
section에서 같은 원소가 두 번 이상 나타나지 않습니다.
section의 원소는 오름차순으로 정렬되어 있습니다.


입출력 예
n m section result
8 4 [2, 3, 6] 2
5 4 [1, 3] 1
4 1 [1, 2, 3, 4] 4

 

▼ 나의 풀이 

function solution(n, m, section) {
  var count = 0;
  let arr = new Array(n).fill(1);
  section.forEach((v) => (arr[v - 1] = 0));
  arr.forEach((v, i) => {
    if (v == 0) {
      count++;
      arr.fill(1, v, i + m);
    }
  });

  return count;
}
  • 1로 채워진 배열 arr 생성 
  • section 배열에 있는 수는 0으로 
  • arr를 돌면서 0이면 count증가 (칠하는 횟수) m만큼 1로 채움 
  • fill에서 의미없이 1로 채우는 것이 비효율적이다.  

▼ painted 변수 활용

function solution(n, m, section) {
  var count = 0;
  var painted = 0;

  for (const s of section) {
    if (painted < s) {
      count++;
      painted = s + m - 1;
    }
  }

  return count;
}
  • 나의 풀이에서 칠해져있는 벽을 표현하기 위해 arr배열을 사용했었는데, 위 방법에서는 그냥 변수 하나를 활용했다.
  • painted는 현재까지 칠해진 벽의 번호를 뜻한다. (처음에는 0)
  • for문으로 section의 번호 하나씩 다루므로 위에서 내가 푼 방법은 벽의 전체 길이만큼 돌아야 하는 것에 비해 매우 효율적이다.
  • 현재 칠해진 벽의 번호(painted)가 s보다 작으면 현재 s번호의 벽이 칠해져있지 않은 것이므로 count를 증가시키고, painted를 s+m-1으로 바꾼다. 
  • 시간효율이 매우 차이 난다!!(나의 풀이는 O(n), 아래 풀이는 O(section.length)인데 항상 section의 길이가 더 작다.)

기사단원의 무기 #136798


문제 설명
숫자나라 기사단의 각 기사에게는 1번부터 number까지 번호가 지정되어 있습니다. 기사들은 무기점에서 무기를 구매하려고 합니다.

각 기사는 자신의 기사 번호의 약수 개수에 해당하는 공격력을 가진 무기를 구매하려 합니다. 단, 이웃나라와의 협약에 의해 공격력의 제한수치를 정하고, 제한수치보다 큰 공격력을 가진 무기를 구매해야 하는 기사는 협약기관에서 정한 공격력을 가지는 무기를 구매해야 합니다.

예를 들어, 15번으로 지정된 기사단원은 15의 약수가 1, 3, 5, 15로 4개 이므로, 공격력이 4인 무기를 구매합니다. 만약, 이웃나라와의 협약으로 정해진 공격력의 제한수치가 3이고 제한수치를 초과한 기사가 사용할 무기의 공격력이 2라면, 15번으로 지정된 기사단원은 무기점에서 공격력이 2인 무기를 구매합니다. 무기를 만들 때, 무기의 공격력 1당 1kg의 철이 필요합니다. 그래서 무기점에서 무기를 모두 만들기 위해 필요한 철의 무게를 미리 계산하려 합니다.

기사단원의 수를 나타내는 정수 number와 이웃나라와 협약으로 정해진 공격력의 제한수치를 나타내는 정수 limit와 제한수치를 초과한 기사가 사용할 무기의 공격력을 나타내는 정수 power가 주어졌을 때, 무기점의 주인이 무기를 모두 만들기 위해 필요한 철의 무게를 return 하는 solution 함수를 완성하시오.

제한사항
1 ≤ number ≤ 100,000
2 ≤ limit ≤ 100
1 ≤ power ≤ limit


입출력 예
number limit power result
5 3 2 10
10 3 2 21

 

▼ 나의 풀이 

function solution(number, limit, power) {
  var count = 0;
  for (let i = 1; i <= number; i++) {
    let dc = divisorCount(i);
    dc > limit ? (count += power) : (count += dc);
  }
  return count;
}

const divisorCount = (num) => {
  let count = 0;
  for (let i = 1; i <= Math.sqrt(num); i++) {
    if (num % i == 0) count += 2;
    if (i == Math.sqrt(num)) count--;
  }
  return count;
};
  • 약수의 개수를 리턴하는 함수 divisorCount 를 정의한다.
    ( 제곱근까지만 for문을 돌림면 되고, i로 나눴을 때 나머지가 0이면 약수는 쌍으로 존재하므로 count를 2개 증가시킨다. 
    단, 1,4,9,16처럼 제곱근이 정수일 경우는 1,2,3,4 같은 숫자는 쌍으로 존재하지 않으므로 count를 1 감소시킨다. )
  • 약수의 개수가 limit 보다 크면 count에 power를 더하고, 아니면 해당 약수의 개수를 count에 증가시킨다. 

[1차] 다트 게임 #17682


문제 설명
다트 게임
카카오톡에 뜬 네 번째 별! 심심할 땐? 카카오톡 게임별~


카카오톡 게임별의 하반기 신규 서비스로 다트 게임을 출시하기로 했다. 다트 게임은 다트판에 다트를 세 차례 던져 그 점수의 합계로 실력을 겨루는 게임으로, 모두가 간단히 즐길 수 있다.
갓 입사한 무지는 코딩 실력을 인정받아 게임의 핵심 부분인 점수 계산 로직을 맡게 되었다. 다트 게임의 점수 계산 로직은 아래와 같다.

다트 게임은 총 3번의 기회로 구성된다.
각 기회마다 얻을 수 있는 점수는 0점에서 10점까지이다.
점수와 함께 Single(S), Double(D), Triple(T) 영역이 존재하고 각 영역 당첨 시 점수에서 1제곱, 2제곱, 3제곱 (점수1 , 점수2 , 점수3 )으로 계산된다.
옵션으로 스타상(*) , 아차상(#)이 존재하며 스타상(*) 당첨 시 해당 점수와 바로 전에 얻은 점수를 각 2배로 만든다. 아차상(#) 당첨 시 해당 점수는 마이너스된다.
스타상(*)은 첫 번째 기회에서도 나올 수 있다. 이 경우 첫 번째 스타상(*)의 점수만 2배가 된다. (예제 4번 참고)
스타상(*)의 효과는 다른 스타상(*)의 효과와 중첩될 수 있다. 이 경우 중첩된 스타상(*) 점수는 4배가 된다. (예제 4번 참고)
스타상(*)의 효과는 아차상(#)의 효과와 중첩될 수 있다. 이 경우 중첩된 아차상(#)의 점수는 -2배가 된다. (예제 5번 참고)
Single(S), Double(D), Triple(T)은 점수마다 하나씩 존재한다.
스타상(*), 아차상(#)은 점수마다 둘 중 하나만 존재할 수 있으며, 존재하지 않을 수도 있다.
0~10의 정수와 문자 S, D, T, *, #로 구성된 문자열이 입력될 시 총점수를 반환하는 함수를 작성하라.

입력 형식
"점수|보너스|[옵션]"으로 이루어진 문자열 3세트.
예) 1S2D*3T

점수는 0에서 10 사이의 정수이다.
보너스는 S, D, T 중 하나이다.
옵선은 *이나 # 중 하나이며, 없을 수도 있다.
출력 형식
3번의 기회에서 얻은 점수 합계에 해당하는 정수값을 출력한다.
예) 37

입출력 예제
예제 dartResult answer 설명
1 1S2D*3T 37 11 * 2 + 22 * 2 + 33
2 1D2S#10S 9 12 + 21 * (-1) + 101
3 1D2S0T 3 12 + 21 + 03
4 1S*2T*3S 23 11 * 2 * 2 + 23 * 2 + 31
5 1D#2S*3S 5 12 * (-1) * 2 + 21 * 2 + 31
6 1T2D3D# -4 13 + 22 + 32 * (-1)
7 1D2S3T* 59 12 + 21 * 2 + 33 * 2

 

▼ 나의 풀이 

function solution(result) {
  var answer = 0;
  var pos = 0;
  var result = result.split("");
  var scores = [];
  result.map((v, i) => {
    if (v == "S" || v == "D" || v == "T") {
      let score = +result.slice(pos, i).join("");
      if (v == "D") score **= 2;
      else if (v == "T") score **= 3;
      if (result[i + 1]) {
        if (result[i + 1] == "*") {
          score *= 2;
          if (scores.length > 0) scores[scores.length - 1] *= 2;
          pos = i + 2;
        } else if (result[i + 1] == "#") {
          score *= -1;
          pos = i + 2;
        } else pos = i + 1;
      }
      scores.push(score);
    }
  });
  return scores[0] + scores[1] + scores[2];
}
  • 조건이 너무 많은데 제대로 읽지 않다가 좀 헤맸다 ..
  • if문이 너무 많아서 보기만해도 어지러운 코드가 되어버렸다. 
  • 정규표현식으로 풀 수 있을 거 같은데 정규표현식 공부를 확실하게 안해서 모르겠다ㅜ
  • 시간복잡도는 나쁘지 않은 것 같다. 

▼ 정규표현식 이용한 풀이 

 

function solution(dartResult) {
  const bonus = { S: 1, D: 2, T: 3 };
  const options = { "*": 2, "#": -1, undefined: 1 };
  let darts = dartResult.match(/\d\d?.?\D/g);
  let scores = [];
  for (let i = 0; i < darts.length; i++) {
    let split = darts[i].match(/(^\d{1,})(S|D|T)(\*|#)?/);
    scores[i] = Math.pow(split[1], bonus[split[2]]) * options[split[3]];
    if (split[3] === "*" && scores[i - 1]) scores[i - 1] *= options["*"];
  }
  return scores[0] + scores[1] + scores[2];
}

/\d\d?.?\D/g:

  • \d는 숫자 0부터 9까지를 의미 (첫번째 d는 항상 있어야하며 두번째 d는 있을 수도 있고 없을 수도 있다. 10을 처리하기 위함)
  • .은 어떤 문자 하나(개행 문자 제외)를 의미
  • ?는 있을수도 있고 없을 수도 있음 
  • \D는 숫자가 아닌 문자 하나와 일치 

✓ 정규표현식에서 소괄호 ()를 사용하여 그룹을 만들면, match() 함수는 전체 매칭 결과뿐만 아니라 각 그룹에 일치하는 부분도 배열로 반환한다. 

/(^\d{1,})(S|D|T)(\*|#)?/
  • ^는 문자열의 시작을 의미한다.
  • \d{1,}는 숫자가 1번 이상 반복되는 부분에 일치한다. 
  • S|D|T는 S,D,T 중 하나에 일치한다. 
  • *|#는 *과 # 중 하나에 일치하며, ?가 붙었으므로 없어도 일치하는 것으로 간주한다. 
  • 위 정규표현식은 ()로 그룹을 지었으므로, match()의 결과로는
    • 정규표현식에 일치하는 전체 문자열
    • 각 그룹에 일치하는 부분
  • 을 리턴한다. 

정규표현식은 너무 어려워 ....

 


로또의 최고 순위와 최저 순위 #77484


문제 설명
로또 6/45(이하 '로또'로 표기)는 1부터 45까지의 숫자 중 6개를 찍어서 맞히는 대표적인 복권입니다. 아래는 로또의 순위를 정하는 방식입니다. 1

순위 당첨 내용
1 6개 번호가 모두 일치
2 5개 번호가 일치
3 4개 번호가 일치
4 3개 번호가 일치
5 2개 번호가 일치
6(낙첨) 그 외
로또를 구매한 민우는 당첨 번호 발표일을 학수고대하고 있었습니다. 하지만, 민우의 동생이 로또에 낙서를 하여, 일부 번호를 알아볼 수 없게 되었습니다. 당첨 번호 발표 후, 민우는 자신이 구매했던 로또로 당첨이 가능했던 최고 순위와 최저 순위를 알아보고 싶어 졌습니다.
알아볼 수 없는 번호를 0으로 표기하기로 하고, 민우가 구매한 로또 번호 6개가 44, 1, 0, 0, 31 25라고 가정해보겠습니다. 당첨 번호 6개가 31, 10, 45, 1, 6, 19라면, 당첨 가능한 최고 순위와 최저 순위의 한 예는 아래와 같습니다.

당첨 번호 31 10 45 1 6 19 결과
최고 순위 번호 31 0→10 44 1 0→6 25 4개 번호 일치, 3등
최저 순위 번호 31 0→11 44 1 0→7 25 2개 번호 일치, 5등
순서와 상관없이, 구매한 로또에 당첨 번호와 일치하는 번호가 있으면 맞힌 걸로 인정됩니다.
알아볼 수 없는 두 개의 번호를 각각 10, 6이라고 가정하면 3등에 당첨될 수 있습니다.
3등을 만드는 다른 방법들도 존재합니다. 하지만, 2등 이상으로 만드는 것은 불가능합니다.
알아볼 수 없는 두 개의 번호를 각각 11, 7이라고 가정하면 5등에 당첨될 수 있습니다.
5등을 만드는 다른 방법들도 존재합니다. 하지만, 6등(낙첨)으로 만드는 것은 불가능합니다.
민우가 구매한 로또 번호를 담은 배열 lottos, 당첨 번호를 담은 배열 win_nums가 매개변수로 주어집니다. 이때, 당첨 가능한 최고 순위와 최저 순위를 차례대로 배열에 담아서 return 하도록 solution 함수를 완성해주세요.

제한사항
lottos는 길이 6인 정수 배열입니다.
lottos의 모든 원소는 0 이상 45 이하인 정수입니다.
0은 알아볼 수 없는 숫자를 의미합니다.
0을 제외한 다른 숫자들은 lottos에 2개 이상 담겨있지 않습니다.
lottos의 원소들은 정렬되어 있지 않을 수도 있습니다.
win_nums은 길이 6인 정수 배열입니다.
win_nums의 모든 원소는 1 이상 45 이하인 정수입니다.
win_nums에는 같은 숫자가 2개 이상 담겨있지 않습니다.
win_nums의 원소들은 정렬되어 있지 않을 수도 있습니다.


입출력 예
lottos win_nums result
[44, 1, 0, 0, 31, 25] [31, 10, 45, 1, 6, 19] [3, 5]
[0, 0, 0, 0, 0, 0] [38, 19, 20, 40, 15, 25] [1, 6]
[45, 4, 35, 20, 3, 9] [20, 9, 3, 45, 4, 35] [1, 1]

 

▼ 나의 풀이 

function solution(lottos, win_nums) {
  const rank = [6, 6, 5, 4, 3, 2, 1];
  let zero = 0;
  let correct = 0;
  for (const num of lottos) {
    if (num == 0) zero++;
    else if (win_nums.indexOf(num) != -1) correct++;
  }
  return [rank[correct + zero], rank[correct]];
}
  • lottos 배열의 숫자 중 0인 갯수와 win_nums에 존재하는 수의 갯수를 각각 zero, correct 에 저장한다. 
  • 인덱스가 맞춘 갯수라면 값이 순위인 배열 rank에 최대 순위인 지워진 숫자가 모두 맞았을 경우(correct+zero), 최저 순위인 지워진 숫자가 모두 틀렸을 경우(correct)를 넣어 순위를 구한다. 

숫자 짝꿍 #131128 


문제 설명
두 정수 X, Y의 임의의 자리에서 공통으로 나타나는 정수 k(0 ≤ k ≤ 9)들을 이용하여 만들 수 있는 가장 큰 정수를 두 수의 짝꿍이라 합니다(단, 공통으로 나타나는 정수 중 서로 짝지을 수 있는 숫자만 사용합니다). X, Y의 짝꿍이 존재하지 않으면, 짝꿍은 -1입니다. X, Y의 짝꿍이 0으로만 구성되어 있다면, 짝꿍은 0입니다.

예를 들어, X = 3403이고 Y = 13203이라면, X와 Y의 짝꿍은 X와 Y에서 공통으로 나타나는 3, 0, 3으로 만들 수 있는 가장 큰 정수인 330입니다. 다른 예시로 X = 5525이고 Y = 1255이면 X와 Y의 짝꿍은 X와 Y에서 공통으로 나타나는 2, 5, 5로 만들 수 있는 가장 큰 정수인 552입니다(X에는 5가 3개, Y에는 5가 2개 나타나므로 남는 5 한 개는 짝 지을 수 없습니다.)
두 정수 X, Y가 주어졌을 때, X, Y의 짝꿍을 return하는 solution 함수를 완성해주세요.

제한사항
3 ≤ X, Y의 길이(자릿수) ≤ 3,000,000입니다.
X, Y는 0으로 시작하지 않습니다.
X, Y의 짝꿍은 상당히 큰 정수일 수 있으므로, 문자열로 반환합니다.
입출력 예
X Y result
"100" "2345" "-1"
"100" "203045" "0"
"100" "123450" "10"
"12321" "42531" "321"
"5525" "1255" "552"

 

프로그래머스 level1문제 중에 첫번째로 해결 못한 문제 등장이다 ....

조건에서  3 ≤ X, Y의 길이(자릿수) ≤ 3,000,000이 있는데, 이게 배열의 범위를 넘어가기 때문에 시간초과 에러가 계속 떴다. 

그래서 아래 나의 코드로 이것저것 수정해가면서 풀었는데 도저히 해결되지 않았다. 

indexOf, repleace, splice 등의 내장함수를 사용하지 않고 풀지 못해서 결국 답을 참고했다. 

 

▼ 나의풀이 (통과x) 

 

function solution(X, Y) {
  let str = "";

  let s = "34677";
  console.log(s.slice(0, 1) + "2" + "0" + s.slice(3));
  for (let i = 0; i < X.length; i++) {
    if (Y.indexOf(X[i]) != -1) {
      str += X[i];
      Y = Y.replace(X[i], "");
    }
  }
  if (str.length < 1) return "-1";
  else if (str[0] == 0) return "0";
  else {
    for (let i = 0; i < str.length; i++) {
      for (let j = 0; j < str.length - 1 - i; j++) {
        if (str[j] < str[j + 1]) {
          let temp1 = str[j];
          let temp2 = str[j + 1];
          let end = j < str.length - 1 ? str.slice(j + 2) : "";
          str = str.slice(0, j) + temp2 + temp1 + end;
        }
      }
    }
    return str;
  }
}

▼ 정답풀이

function solution(X, Y) {
  const xObj = {},
    yObj = {};

  X.split("").forEach((x) => {
    if (xObj[x] === undefined) xObj[x] = 1;
    else xObj[x]++;
  });

  Y.split("").forEach((y) => {
    if (yObj[y] === undefined) yObj[y] = 1;
    else yObj[y]++;
  });

  const concatObj = {};
  Object.entries(xObj).forEach(([key, value]) => {
    if (yObj[key] !== undefined) concatObj[key] = Math.min(value, yObj[key]);
  });

  let concatStr = "";
  Object.entries(concatObj).forEach(([key, value]) => {
    concatStr = concatStr.concat(key.repeat(value));
  });

  let concatArr = concatStr.split("").sort((a, b) => b - a);
  if (concatArr.length === 0) return "-1";
  else if (concatArr[0] === "0") return "0";
  else return concatArr.join("");
}
  • 빈 객체 xObj, yObj를 선언하고, 각각 forEach로 돌면서 해당 숫자에 따라 값을 증가시킨다. 
    (객체의 최대 길이가 10이다. )
  • 또다른 빈 객체 concatObj를 선언하고,
    xObj를 forEach로 돌면서, (객체를 forEach로 쓰려면 Object.entries필요) yObj에 해당값이 있을 경우, 두 객체를 비교해 더 작은 값을 concatObj에 넣는다. 
  • concatObj를 forEach로 돌면서 key값을 value값만큼 repeat하여 concatStr에 넣어준다. 
  • 마지막으로 리턴값을 위해 concatStr을 split을 한 후 sort를 한 배열을  concatArr로 정의한다.
  • concatArr의 길이가 0이면 "-1"을 리턴
    concatArr의 첫번째 값이 0이면 "0"을 리턴
    아니면, concatArr을 join한 값을 리턴한다. 

난 시간을 며칠을 더 줘도 저런 생각을 못했을지도 모른다. 

좋은 삽질이었다고 생각하고, 잘 기억하다가 나중에 또 써먹자.


옹알이 (2) #133499 


문제 설명
머쓱이는 태어난 지 11개월 된 조카를 돌보고 있습니다. 조카는 아직 "aya", "ye", "woo", "ma" 네 가지 발음과 네 가지 발음을 조합해서 만들 수 있는 발음밖에 하지 못하고 연속해서 같은 발음을 하는 것을 어려워합니다. 문자열 배열 babbling이 매개변수로 주어질 때, 머쓱이의 조카가 발음할 수 있는 단어의 개수를 return하도록 solution 함수를 완성해주세요.

제한사항
1 ≤ babbling의 길이 ≤ 100
1 ≤ babbling[i]의 길이 ≤ 30
문자열은 알파벳 소문자로만 이루어져 있습니다.
입출력 예
babbling result
["aya", "yee", "u", "maa"] 1
["ayaye", "uuu", "yeye", "yemawoo", "ayaayaa"] 2

 

▼ 나의 풀이 

function solution(babbling) {
  var answer = 0;
  let possible = ["aya", "ye", "woo", "ma"];
  for (const b of babbling) {
    let tmp = "",
      before = "";
    for (let i = 0; i < b.length; i++) {
      tmp += b[i];
      let idx = possible.indexOf(tmp);
      if (idx != -1 && tmp != before) {
        before = tmp;
        tmp = "";
      }
    }
    if (tmp === "") answer++;
  }
  return answer;
}
  • 코드만 봤을 때는 조금 지저분한 코드 같이 생겼지만..!
  • input으로 들어온 babbling 배열을 for문으로 돌면서
    (비교를 위해 사용하는 tmp 문자열과 직전에 나온 값을 저장해 비교하는 before을 빈 문자열로 선언한다.)
  • 문자열 하나를 또 다른 for문으로 돌면서,
    tmp에 한글자씩 추가하면서 발음 가능한 배열인 possible에 tmp가 있는 지 indexOf로 비교한다. 
  • 있으면 해당 인덱스가 -1이 아니면서 , 직전에 나온 값 before가 아닌지 확인하고,
    before에 tmp를 넣고, tmp는 다시 빈 문자열로 바꿔준다.  
  • 한 문자열을 다 확인하고 나서 tmp가 빈 문자열이면 모든 단어들이 발음할 수 있다는 것이므로 answer에 1을 증가시킨다. 

▼ 정규표현식 이용한 풀이 

function solution(babbling) {
  const regexp1 = /(aya|ye|woo|ma)\1+/;
  const regexp2 = /^(aya|ye|woo|ma)+$/;

  return babbling.reduce(
    (ans, word) => (!regexp1.test(word) && regexp2.test(word) ? ++ans : ans),
    0
  );
}
  • (aya|ye|woo|ma)\1+ : 해당 문자열 중 한 번 이상 반복되는 패턴을 찾는다. (연속되게 오는 경우를 제외시키기 위힘)
  • ^(aya|ye|woo|ma)+$ : 하나 또는 그 조합으로만 이루어져 있는 패턴을 찾는다. 
    ^ : 문자열의 시작 , $ :문자열의 끝 
  • regexp1에 해당하지 않고, regexp2에 해당하면 ans를 1씩 증가시킨다. 

정규표현식은 아직도 익숙하지 않다.

공부를 제대로 안했거든 


체육복 #42862


문제 설명
점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다. 예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.

전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost, 여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때, 체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.

제한사항
전체 학생의 수는 2명 이상 30명 이하입니다.
체육복을 도난당한 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
여벌의 체육복을 가져온 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
여벌 체육복이 있는 학생만 다른 학생에게 체육복을 빌려줄 수 있습니다.
여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.


입출력 예
n lost reserve return
5 [2, 4] [1, 3, 5] 5
5 [2, 4] [3] 4
3 [3] [1] 2

 

▼ 나의 풀이 

function solution(n, lost, reserve) {
  var answer = n - lost.length;
  lost.map((v, i) => {
    if (reserve.indexOf(v) != -1) {
      reserve.splice(reserve.indexOf(v), 1);
      lost[i] = -1;
      answer++;
    }
  });
  lost
    .sort((a, b) => a - b)
    .map((v) => {
      if (reserve.indexOf(v - 1) != -1) {
        reserve.splice(reserve.indexOf(v - 1), 1);
        answer++;
      } else if (reserve.indexOf(v + 1) != -1) {
        reserve.splice(reserve.indexOf(v + 1), 1);
        answer++;
      }
    });
  return answer;
}
  • 먼저, 전체 학생에서 lost에 있는 학생을 제외한 수만큼은 무조건 체육복이 있으므로 그 값으로 answer의 초기값을 정한다. 
  • lost 배열에 있는 학생은 자신의 여벌은 자신이 입어야하므로,  reserve 배열에 해당 값이 있다면, reserve 배열에서 해당 값을 splice로 지우고, lost 배열에서 해당 값을 -1로 바꾸고,  answer을 1 증가시킨다. 
  • lost 배열이 오름차순 정렬 되어있어야 앞에 있는 학생부터 자신보다 1작은 값과 비교하므로
    sort한 후 map을 돌리면서 해당 값에 1을 뺀 값이 reserve에 있으면, reserve에서 해당 값을 지우고, answer을 1증가시킨다. 
  • 해당 값에 1을 더한 값이 reserve에 있으면, reserve에서 해당 값을 지우고, answer을 1증가시킨다. 

▼ 다른 사람의 풀이 

function solution(n, lost, reserve) {
  const students = {};
  let answer = 0;
  for (let i = 1; i <= n; i++) {
    students[i] = 1;
  }
  lost.forEach((number) => (students[number] -= 1));
  reserve.forEach((number) => (students[number] += 1));

  for (let i = 1; i <= n; i++) {
    if (students[i] === 2 && students[i - 1] === 0) {
      students[i - 1]++;
      students[i]--;
    } else if (students[i] === 2 && students[i + 1] === 0) {
      students[i + 1]++;
      students[i]--;
    }
  }

  for (let key in students) {
    if (students[key] >= 1) {
      answer++;
    }
  }

  return answer;
}
  • 가독성도 좋고 직관적인 풀이인 것 같다.
  • 먼저, 학생 번호를 key값으로 갖고, value가 모두 1인 객체 students를 초기화 시킨다. 
  • lost를 forEach로 돌면서 lost 배열 안에 있는 students 객체의 value를 1 감소시킨다. 
  • reserve를 forEach로 돌면서 reserve 배열 안에 있는 students 객체의 value를 1 증가시킨다. 
  • 그러고 나서 ,students 객체를 for문으로 도는데, 해당 value가 2이면서, 직전 value가 0이면 
    해당 value는 1감소시키고, 직전 value는 1 증가시킨다. 
  • 해당 value가 2이면서, 직후 value가 0이면 해당 value는 1감소시키고, 직후 value는 1 증가시킨다. 
  • 마지막으로, students를 for문으로 돌면서 1 이상의 value를 가지고 있을 경우 answer를 1씩 증가시킨다.

완주하지 못한 선수 #42576


문제 설명
수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.

마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.

제한사항
마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.
completion의 길이는 participant의 길이보다 1 작습니다.
참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
참가자 중에는 동명이인이 있을 수 있습니다.


입출력 예
participant completion return
["leo", "kiki", "eden"] ["eden", "kiki"] "leo"
["marina", "josipa", "nikola", "vinko", "filipa"] ["josipa", "filipa", "marina", "nikola"] "vinko"
["mislav", "stanko", "mislav", "ana"] ["stanko", "ana", "mislav"] "mislav"


완주하지 못한 선수 #42576


문제 설명
수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.

마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.

제한사항
마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.
completion의 길이는 participant의 길이보다 1 작습니다.
참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
참가자 중에는 동명이인이 있을 수 있습니다.


입출력 예
participant completion return
["leo", "kiki", "eden"] ["eden", "kiki"] "leo"
["marina", "josipa", "nikola", "vinko", "filipa"] ["josipa", "filipa", "marina", "nikola"] "vinko"
["mislav", "stanko", "mislav", "ana"] ["stanko", "ana", "mislav"] "mislav"

 

▼ 효율성 테스트 통과못한 풀이 (정답x) 

function solution(participant, completion) {
  for (const p of participant) {
    const idx = completion.indexOf(p);
    if (idx != -1) completion.splice(idx, 1);
    else return p;
  }
}

 

▼ 효율성 테스트 통과한 풀이 (정답o) 

function solution(participant, completion) {
  let obj = new Object();
  for (const p of participant) {
    obj[p] === undefined ? (obj[p] = 1) : obj[p]++;
  }
  for (const c of completion) {
    obj[c] == 1 ? delete obj[c] : obj[c]--;
  }
  return Object.keys(obj).join("");
}
  • 배열은 순서가 있기 때문에 순회하는 데 매우 오랜 시간이 걸린다 
  • > Object를 이용하자 !
  • participant 배열을 순회하면서 해당하는 해당 값이 없으면 1로 저장, 있으면 1 증가시킨다. 
  • completion 배열을 순회하면서 obj에 해당 값이 1이면 지워주면 되므로 delete하고, 1이 아니면 1 감소만 시켜준다.
  • 최종적으로 object에는 단 하나의 값만 존재하기 때문에 keys값을 모두 받아와서 join("")해주면 답이다. 

 

▼ 자바스크립트 객체와 배열의 시간복잡도 

https://s0ojin.tistory.com/60


문자열 나누기 #140108


문제 설명
문자열 s가 입력되었을 때 다음 규칙을 따라서 이 문자열을 여러 문자열로 분해하려고 합니다.

먼저 첫 글자를 읽습니다. 이 글자를 x라고 합시다.
이제 이 문자열을 왼쪽에서 오른쪽으로 읽어나가면서, x와 x가 아닌 다른 글자들이 나온 횟수를 각각 셉니다. 처음으로 두 횟수가 같아지는 순간 멈추고, 지금까지 읽은 문자열을 분리합니다.
s에서 분리한 문자열을 빼고 남은 부분에 대해서 이 과정을 반복합니다. 남은 부분이 없다면 종료합니다.
만약 두 횟수가 다른 상태에서 더 이상 읽을 글자가 없다면, 역시 지금까지 읽은 문자열을 분리하고, 종료합니다.
문자열 s가 매개변수로 주어질 때, 위 과정과 같이 문자열들로 분해하고, 분해한 문자열의 개수를 return 하는 함수 solution을 완성하세요.

제한사항
1 ≤ s의 길이 ≤ 10,000
s는 영어 소문자로만 이루어져 있습니다.


입출력 예
s result
"banana" 3
"abracadabra" 6
"aaabbaccccabba" 3

 

▼ while문을 사용한 나의 풀이 

function solution(s) {
  let answer = 1;
  let pos = 0;
  while (pos < s.length - 1) {
    let count = 0;
    for (let i = pos; i < s.length; i++) {
      s[i] === s[pos] ? count++ : count--;
      if (count == 0 || i == s.length - 1) {
        pos = i + 1;
        break;
      }
    }
    if (pos < s.length) answer++;
  }
  return answer;
}
  • while문을 돌려서 성능이 좋지 않다. 
  • while문을 써서 해결할 생각만 하느라 코드가 더 복잡해지고 가독성이 떨어졌다.

▼ for문만 사용한 풀이 

function solution(s) {
  let answer = 0;
  let current;
  let count = 0;

  for (let i = 0; i < s.length; i++) {
    if (count === 0) {
      answer++;
      count++;
      current = s[i];
    } else {
      current === s[i] ? count++ : count--;
    }
  }

  return answer;
}
  • count는 첫번째 글자와 같으면 증가시키고, 아니면 감소시키기 위한 변수이다. 
  • count가 0이면 나눌 문자열이 있다는 것이므로 answer를 증가시키고, count를 증가시키며, 
    비교할 문자인 current를 해당 값으로 넣는다. 
  • count가 0이 아니면 아직 문자열을 나눌 수 없고, 현재 값과 비교할 문자 current를 비교하여 같으면 1 증가시키고, 다르면 1 감소시킨다.

난 비교할 값을 pos에 index를 넣어주어서 했는데 저렇게 값 자체를 넣어주는 것이 더 깔끔해보인다. 

접근은 비슷했으나 좀 더 깊은 생각이 필요했다. 

 

 

profile

DEVELOP

@JUNGY00N