[Programmers] 72411 메뉴 개발

Lee Jooam·2022년 4월 25일
0
post-custom-banner

출처: 프로그래머스 코딩테스트 연습

코딩 테스트는 지문을 해석하는 능력도 중요한 것 같습니다.

자신이 구현해야 할 기능, 예외사항 등 올바른 알고리즘을 구현하는데 필요한 요소들이 모두 지문에 숨겨져 있습니다.

국어 비문학 문제를 보는 것 같지만 제품의 요구사항을 판단하는 능력이라고 생각하고 열심히 하는 중입니다.

요구 사항

문제가 요구하는 것은 다음과 같습니다.

  1. 주어진 String과 Number를 통해 조합을 생성
  2. 구한 조합을 지속적으로 저장하며 counting
  3. 누적된 정보에서 counting이 2인 이상인 조합 중 가장 최대치인 요소 찾기

주의 사항

  1. 결과를 추출할 때 오름차순으로 sort
  2. 주어진 String이 항상 오름차순으로 들어오는 게 아님

구현

function solution(orders, course) {
  function getCombination(combAnswer, menu, fixed, select, index) {
    if (select === 0) {
      combAnswer.push(fixed);
      return;
    }

    for (let i = index; i < menu.length; i++) {
      if (menu.length - i < select) break;
      getCombination(combAnswer, menu, [...fixed, menu[i]], select - 1, i + 1);
    }
  }

  const answer = [];
  const info = {};
  const resultInfo = {};

  for (let c of course) {
    resultInfo[c] = {
      menu: "",
      counter: 0,
    };
  }

  for (let menu of orders) {
    const combAnswer = [];

    for (let c of course) {
      getCombination(combAnswer, menu.split("").sort().join(""), [], c, 0);
    }

    for (let comb of combAnswer) {
      if (!info[comb.join("")]) info[comb.join("")] = 0;

      info[comb.join("")]++;
    }
  }

  for (let menu in info) {
    if (info[menu] < 2) continue;

    if (resultInfo[menu.length].counter < info[menu]) {
      resultInfo[menu.length].menu = menu;
      resultInfo[menu.length].counter = info[menu];
    } else if (resultInfo[menu.length].counter == info[menu]) {
      resultInfo[menu.length].menu += `,${menu}`;
    }
  }

  for (let result in resultInfo) {
    if (resultInfo[result].menu.length) {
      answer.push(...resultInfo[result].menu.split(","));
    }
  }

  return answer.sort();
}

const orders = ["XYZ", "XWY", "WXA"];
const course = [2, 3, 4];

solution(orders, course);
  function getCombination(combAnswer, menu, fixed, select, index) {
    if (select === 0) {
      combAnswer.push(fixed);
      return;
    }

    for (let i = index; i < menu.length; i++) {
      if (menu.length - i < select) break;
      getCombination(combAnswer, menu, [...fixed, menu[i]], select - 1, i + 1);
    }
  }

조합을 구하는 함수입니다. 순열, 조합 알고리즘 구현을 토대로 작성했습니다.

외부 스코프에 있는 combAnswer 참조를 인자로 받아 계속 값을 누적시킵니다.

  const answer = [];
  const info = {};
  const resultInfo = {};

  for (let c of course) {
    resultInfo[c] = {
      menu: "",
      counter: 0,
    };
  }

  for (let menu of orders) {
    const combAnswer = [];

    for (let c of course) {
      getCombination(combAnswer, menu.split("").sort().join(""), [], c, 0);
    }

    for (let comb of combAnswer) {
      if (!info[comb.join("")]) info[comb.join("")] = 0;

      info[comb.join("")]++;
    }
  }

orders마다 course로 들어온 수만큼 경우의 수를 구합니다. 그리고 생성된 조합을 info object에 맵핑하고 그 count를 기록했습니다.

resultInfo는 동일한 count를 가지는 경우의 수가 있을 수 있기 때문에 추후 결과값 처리를 위해 선언했습니다.

 for (let menu in info) {
    if (info[menu] < 2) continue;

    if (resultInfo[menu.length].counter < info[menu]) {
      resultInfo[menu.length].menu = menu;
      resultInfo[menu.length].counter = info[menu];
    } else if (resultInfo[menu.length].counter == info[menu]) {
      resultInfo[menu.length].menu += `,${menu}`;
    }
  }

  for (let result in resultInfo) {
    if (resultInfo[result].menu.length) {
      answer.push(...resultInfo[result].menu.split(","));
    }
  }

  return answer.sort();
}

구한 결과들을 처리해서 정답을 만듭니다. 일단 info에서 두 번 이상 주문되지 않은 것들은 continue 처리합니다.

resultInfo는 메뉴의 길이별로 최대로 주문된 조합들을 저장합니다. 만약 count가 같다면 문자열 덧셈을 통해 결과를 누적합니다.

마지막으로 resultInfo에 있던 결과들을 answer에 저장하고 오름차순으로 sort한 결과를 return합니다.

후기

만들면서 긴가민가한 생각이 많이 들었습니다. 너무 스파게티 코드라고 생각했는데 제출 후 다른 사람들의 정답을 보니 대부분 비슷하게 구현했습니다.

지저분하게 만들 수 밖에 없는 코드일까요?

기능들을 함수로 나눠서 구현했으면 더 가독성이 좋아질 것 같습니다.

profile
프론트엔드 개발자로 걸어가는 중입니다.
post-custom-banner

0개의 댓글