Logo

Changchangwoo's blog

TIL
Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec
Sun
Mon
Tue
Wed
Thu
Fri
Sat
Today I Learned를 기록하는 공간입니다. 🔥🔥🔥
잔디를 클릭하면 내용확인이 가능합니다.

프로그래머스 고득점 kit 그리디 복습

Array.indexOf(value)

  • 배열에 있는 특정 값에 대한 인덱스 반환
  • 없으면 -1, 있으면 가장 가까운 인덱스

체육복 문제

  • 초기 접근 ⇒ filter 메소드를 통해 중복되는 부분 제거 (비효율) , set을 통해 순회한 값 판단
function solution(n, lost, reserve) {
  const lostSet = new Set(lost);
  const reserveSet = new Set(reserve);

  for (const v of reserve) {
    if (lostSet.has(v)) {
      lostSet.delete(v);
      reserveSet.delete(v);
    }
  }

  for (const v of lostSet) {
    if (reserveSet.has(v - 1)) {
      reserveSet.delete(v - 1);
    } else if (reserveSet.has(v + 1)) {
      reserveSet.delete(v + 1);
    } else {
      n--;
    }
  }

  return n;
}

⇒ Set, Map등 다양한 자료구조를 사용한다면 훨씬 더 직관적이고 효율적인 구현이 가능

⇒ 특히 체육복문제와 같이 독립적인 값에서의 중복제거는 set이 매우 이상적

리스트로 답이 없을 것 같다 싶으면, 다른 컬렉션 구조를 한번 보자

조이스틱 문제

  • 초기 접근
    • 커서의 알파벳 개수를 찾고, 우측과 좌측중 당장 가장 가까운 곳을 식별 후 이동하도록 구현
    • 이러면 "BBBAAAAAAAB" 다음과 같은 예제 해결 불가 ⇒ 틀림
/* 
- 'N' 보다 클 부터는 이전 알파벳으로 이동하는게 더 빠름
- 작성 후, 우측과 좌측 중 가장 빠르게 작성할 수 있는 곳 식별
*/
function solution(name) {
  const alpha = [
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4,
    3, 2, 1,
  ];
  let str = new Array(name.length).fill("A");
  let cursor = 0;
  let count = 0;
  while (true) {
    let code = name[cursor].charCodeAt(0) - 65;
    count += alpha[code];
    str[cursor] = name[cursor];
    if (str.join("") === name) break;

    let checkLR = checkLeftRight(str, name, cursor);
    count += Math.abs(checkLR);

    if (cursor + checkLR < 0) cursor = str.length + checkLR;
    else cursor = cursor + checkLR;
  }
  return count;
}

function checkLeftRight(str, name, cursor) {
  let right_count = 0;
  let left_count = 0;
  let idx = cursor;
  while (true) {
    right_count++;
    idx++;
    if (str[idx] !== name[idx]) break;
  }
  idx = cursor;
  while (true) {
    left_count++;
    idx--;
    if (idx === -1) idx = name.length - 1;
    if (str[idx] !== name[idx]) break;
  }
  return right_count <= left_count ? right_count : -left_count;
}
  • 정답 코드 ( GPT )
function solution(name) {
  const alpha = [
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4,
    3, 2, 1,
  ];

  let count = 0;

  // 1️⃣ 알파벳 변경 비용 (네 로직 그대로)
  for (let i = 0; i < name.length; i++) {
    const code = name[i].charCodeAt(0) - 65;
    count += alpha[code];
  }

  // 2️⃣ 커서 이동 최소값 계산 (checkLR의 역할을 승격)
  let move = name.length - 1;

  for (let i = 0; i < name.length; i++) {
    let next = i + 1;

    // 연속된 A 스킵
    while (next < name.length && name[next] === "A") {
      next++;
    }

    // 👉 네가 while에서 하려던 판단을 "수식"으로 한 번에
    const goRightThenLeft = i * 2 + (name.length - next);
    const goLeftThenRight = i + (name.length - next) * 2;

    move = Math.min(move, goRightThenLeft, goLeftThenRight);
  }

  return count + move;
}

⇒ 이 문제의 핵심풀이

커서가 한 번만 방향을 바꾼다는 성질을 이용해, 그 전환 지점을 전부 후보로 두고 각 경우의 이동 비용을 계산한 뒤 최솟값을 선택하는 문제

실화냐;;;; 개어렵다;;; 접근 방식은 알았으니 다음에 다시 풀자

큰 수 만들기

  • 초기 접근
    • 제한 조건 숫자가 100만, 조합 사용 불가능 ⇒ 앞에서부터 순차적으로 탐색
    • 탐색한 수 보다 더 큰수가 있다면 거기까지 자르고 k를 사용할 수 있을 만큼 작은 수 세기 ⇒ 효율성 X
function solution(number, k) {
  var answer = "";
  const stack = [];
  for (let digit of number) {
    while (stack.length && k > 0 && stack[stack.length - 1] < digit) {
      stack.pop();
      k--;
    }
    stack.push(digit);
  }

  return stack.join("").slice(0, stack.length - k);
}

⇒ 이 문제 핵심풀이

  • 순차적으로 탐색하면서 스택을 사용해서 값 저장.
  • 스택의 마지막 값 보다 현재 값이 더 크다면 값을 가지고 있을 이유가 없기에 k가 남아있는 만큼 버릴 수 있음
  • 전부 순회 후 k가 남아 있다 ⇒ 앞에 값들이 이미 더 큰값으로 채워져있음
  • 남은 k만큼 꼬리 제거

이것도 다시풀어 보자