본문 바로가기
Development/자료구조 & 알고리즘

[알고리즘] 프로그래머스 - 롤케이크 자르기 (JS)

by heondeam 2023. 9. 8.

 

✅ 문제

철수는 롤케이크를 두 조각으로 잘라서 동생과 한 조각씩 나눠 먹으려고 합니다. 이 롤케이크에는 여러가지 토핑들이 일렬로 올려져 있습니다. 철수와 동생은 롤케이크를 공평하게 나눠먹으려 하는데, 그들은 롤케이크의 크기보다 롤케이크 위에 올려진 토핑들의 종류에 더 관심이 많습니다. 그래서 잘린 조각들의 크기와 올려진 토핑의 개수에 상관없이 각 조각에 동일한 가짓수의 토핑이 올라가면 공평하게 롤케이크가 나누어진 것으로 생각합니다.

예를 들어, 롤케이크에 4가지 종류의 토핑이 올려져 있다고 합시다. 토핑들을 1, 2, 3, 4와 같이 번호로 표시했을 때, 케이크 위에 토핑들이 [1, 2, 1, 3, 1, 4, 1, 2] 순서로 올려져 있습니다. 만약 세 번째 토핑(1)과 네 번째 토핑(3) 사이를 자르면 롤케이크의 토핑은 [1, 2, 1], [3, 1, 4, 1, 2]로 나뉘게 됩니다. 철수가 [1, 2, 1]이 놓인 조각을, 동생이 [3, 1, 4, 1, 2]가 놓인 조각을 먹게 되면 철수는 두 가지 토핑(1, 2)을 맛볼 수 있지만, 동생은 네 가지 토핑(1, 2, 3, 4)을 맛볼 수 있으므로, 이는 공평하게 나누어진 것이 아닙니다. 만약 롤케이크의 네 번째 토핑(3)과 다섯 번째 토핑(1) 사이를 자르면 [1, 2, 1, 3], [1, 4, 1, 2]로 나뉘게 됩니다. 이 경우 철수는 세 가지 토핑(1, 2, 3)을, 동생도 세 가지 토핑(1, 2, 4)을 맛볼 수 있으므로, 이는 공평하게 나누어진 것입니다. 공평하게 롤케이크를 자르는 방법은 여러가지 일 수 있습니다. 위의 롤케이크를 [1, 2, 1, 3, 1], [4, 1, 2]으로 잘라도 공평하게 나뉩니다. 어떤 경우에는 롤케이크를 공평하게 나누지 못할 수도 있습니다.

롤케이크에 올려진 토핑들의 번호를 저장한 정수 배열 topping이 매개변수로 주어질 때, 롤케이크를 공평하게 자르는 방법의 수를 return 하도록 solution 함수를 완성해주세요.

 

✅ 풀이

처음 생각한 풀이는 다음과 같다.

  • 케이크를 두 개의 큐로 나눈다. (동생의 케이크는 처음에 빈 큐, 형의 케이크는 모든 케이크가 들어있다.)
  • 형의 케이크 큐에서 한 조각씩 동생의 큐로 enqueue한다. 이 때마다 토핑의 종류를 세어 비교한다.
  • 같다면 answer += 1 해준다.

역시나 시간초과, 시간 복잡도를 줄이기 위해 해시 테이블을 사용해야 된다고 생각했다.

 

function solution(topping) {
  let answer = 0;
  let left = [];
  let right = [...topping];
  
  for (let i = 0; i < topping.length - 1; i ++) {
    left.push(right.shift());

    if ([...new Set(left)].length === [...new Set(right)].length) {
      answer += 1;
    }
  }
  

  

  return answer;
}

solution([1, 2, 3, 1, 4]);

 

아래는 해시 테이블 이용한 정답 코드이다. (중복 제거하는 Set 이용 시 시간 복잡도가 증가하는 것으로 판단하였다.)

 

function solution(topping) {
  let answer = 0;
  let leftHash = {};
  let rightHash = {};
  let leftSort = 0;
  let rightSort = 0;
  
  for (const tp of topping) {
    if (rightHash[tp]) {
      rightHash[tp] += 1;
    }else {
      rightHash[tp] = 1;
      rightSort += 1;
    }
  }
  
  for (let i = 0; i < topping.length; i ++) {
    
    if (leftHash[topping[i]]) {
      leftHash[topping[i]] += 1;
    }else {
      leftHash[topping[i]] = 1;
      leftSort += 1;
    }
    
    if(rightHash[topping[i]] - 1) {
      rightHash[topping[i]] -= 1;
    }else {
      delete rightHash[topping[i]];
      rightSort -= 1;
    }
  
      
    if (leftSort === rightSort) {
      answer += 1;
    }
  }
  
  return answer;
}

solution([1, 2, 1, 3, 1, 4, 1, 2]);

 

내가 문제에 접근할 때 고쳐야 할 점을 찾았다.

항상 생각나는 풀이법으로 먼저 접근하고 조금씩 고쳐나가는 편인데, 이런 과정이 나쁘지는 않지만 시간이 정해져 있는 코딩 테스트에서는 조금 비효율적인 것 같다는 생각이 들었다.

앞으로는 제한 사항을 읽고 시간복잡도를 파악한 후에 더나은 자료구조와 알고리즘을 생각해내고, 접근해야 겠다고 느꼈다.

댓글