210811_문제노트_순열

Bitnara Lee·2021년 8월 11일
0

새로운 치킨 소스 레시피

문제

개업 이래로 항상 승승장구하는 '승승장구 치킨집'의 비결은 소스에 있다. 수많은 타사 브랜드 치킨집들이 승승장구 치킨집의 소스 비결을 알아내려고 했으나 빈번히 포기했다.
그 이유는 5대째 내려오는 '비밀의 승승장구 치킨 소스 비율 레시피'는 70억 인구 중 사장님만 알고 있기 때문이다. 최근, 누리꾼 사이에서 이 레시피의 일부분을 발췌했다는 소문을 듣게 되었다.
그 소문은 다음과 같다.

N 가지의 재료 중에 단 M 가지만을 사용하여 조합한 모든 경우의 수 중 하나이다.
재료는 0과 1로만 이루어진 숫자로 암호화가 되어 있고, 항상 1로 시작하며 복호화를 할 수 없다.
단, 0이 3개 이상인 재료는 상한 재료이기 때문에 제외한다.
재료의 순서에 따라 맛이 달라지기 때문에, 재료를 넣는 순서가 다르다면 다른 레시피이다.
이 소문을 참고하여 '비밀의 승승장구 치킨 소스'가 될 수 있는 경우의 수를 모두 반환하는 함수를 작성하세요.

입력

인자 1: stuffArr

  • Number 타입의 재료를 담은 배열
    • 요소는 0과 1로만 이루어진 숫자이며, 항상 1로 시작합니다.
    • 요소는 중복될 수 없습니다.
    • 요소의 길이는 20 이하입니다.
    • 배열의 길이는 2 이상 10 이하입니다.
    • ex) [111, 110, 1010, 10, 10110]

인자 2: choiceNum

  • Number 타입의 1 이상 stuffArr 길이 이하의 자연수
  • 재료를 선택할 수 있는 수를 뜻합니다.
  • ex) 2

출력

  • Number 타입을 반환해야 합니다.
    • stuffArr가 [1, 10, 11000, 1111] 이고, choiceNum은 2라면 사용 가능한 재료는 [1, 10, 1111] 입니다. 조합할 수 있는 경우의 수는 6 가지입니다.

주의사항

  • 만약, 주어진 재료 모두 사용할 수 없다면 빈 배열[]을 반환해야 합니다.
  • 만약, 사용할 수 있는 재료가 choiceNum보다 작다면 빈 배열[] 을 반환해야 합니다.
  • 조합 및 요소는 작은 숫자 -> 큰 숫자로 정렬합니다.
    • 예시로 [1, 10, 11000, 1111]이 요소로 들어왔다면, 0이 세 개인 11000을 제외하고 [1, 10, 1111] 순서가 되어야 하며,
      [ [1, 10], [1, 1111], [10, 1], [10, 1111], [1111, 1], [1111, 10] ]을 반환해야 합니다.

입출력 예시

const output1 = newChickenRecipe([1, 10, 1100, 1111], 2);
console.log(output1);
/*
  [
    [1, 10], [1, 1100], [1, 1111],
    [10, 1], [10, 1100], [10, 1111],
    [1111, 1], [1111, 10], [1111, 1100]
  ]
*/

const output2 = newChickenRecipe([10000, 10, 1], 3);
console.log(output2); // []

< CODE >

function newChickenRecipe(stuffArr, choiceNum) {
  // stuffArr에서 0이 3개 이상이라면 전부 필터로 거르기.
  let freshArr = [];

  for (let i = 0; i < stuffArr.length; i++) {
    const element = String(stuffArr[i])
      .split('')
      .filter((e) => e === '0');
    if (element.length <= 2) {
      freshArr.push(stuffArr[i]);
    }
  }

 /*  내 코드(이렇게도 가능)
 
 stuffArr = stuffArr.filter(stuff => { // 필터해서 나와야할 것은 0 세개인 요소를 제외한 배열 
     strstuff = String(stuff).split('')
     let count = strstuff.filter(el => el === '0').length
     return count < 3 //or stuff 속 3의 length가 <3
   })   */

  
  
  // 정렬
  freshArr.sort((a, b) => a - b);

  // 엣지 케이스 처리
  if (freshArr.length === 0 || freshArr.length < choiceNum) return [];

  // 레시피 초기화
  let result = [];

  // freshArr를 상대로 순열 구하기
  const permutation = (arr, bucket, n) => {
    if (n === 0) {
      result.push(bucket);
      return;
    }

    for (let i = 0; i < arr.length; i++) {
      // 하나를 초이스함
      const choice = arr[i];
      // 배열을 슬라이스함
      const sliceArr = arr.slice();
      // 초이스만 뺀다
      sliceArr.splice(i, 1);
      // 재귀
      permutation(sliceArr, bucket.concat(choice), n - 1);
    }
  };

  // 실행
  permutation(freshArr, [], choiceNum);
  
  // 순열의 길이 반환
  return result;
}


/* 내 코드(bucket.length가 choiceNum이 될때를 리턴 조건으로 줄 수도 있다)

 const recurs = (arr=stuffArr, bucket=[])=>{
       if(bucket.length === choiceNum){
         result.push(bucket)
         return;
       }
       for(let i=0; i< arr.length; i++){
       let clone = arr.slice()    // 이걸 안해주면 나중에 회귀할때 돌아오지 못한다. 그냥 []
       let choice = clone.splice(i,1)
       recurs(clone, bucket.concat(choice))
       } 
   }
   */

배열에 대해 중복순열이 아닌 순열을 구할시

🔰 중복되지 않도록 넣어주는 i번째를 제거해야 한다.

🔰 재귀의 인자로 기존배열과 choiceNum, 요소들 담을 배열(혹은 담을 배열과 기존배열)을 받아
for문 안에서 기존 배열을 slice() 한 후 splice(i,1)로 하나씩 뽑는다.

🔰 그 후 요소들을 담아서 재귀시킨다.

for문을 돌며 i가 해당 숫자일때가 기준이 되고 각각 slice, splice를 거쳐 0번째 요소들이 뒤에 붙는다
이후 조건 충족되며 차례로 리턴되고, 마지막 i번째 요소 리턴,
그 다음 for문의 i번째가 기준이 되고 반복
for문이 다 돌면 끝

profile
Creative Developer

0개의 댓글