kakasoo

[node.js] 메뉴 리뉴얼 ( 프로그래머스 레벨2 ) 본문

프로그래밍/알고리즘 풀이

[node.js] 메뉴 리뉴얼 ( 프로그래머스 레벨2 )

카카수(kakasoo) 2021. 6. 30. 09:48
반응형
/**
 * 1. 단품 메뉴들의 합집합을 만든다.
 * 2. 단품 메뉴들의 조합을 만든다. ( 중복은 제거하고, length가 2 이상. )
 * 3. 조합들과 손님의 주문을 각각 비교하여 횟수를 구한다.
 * 4. 각 팔린 횟수 중에서 가장 메뉴 가짓수length가 큰 것을 뽑는다.
 */

const sortString = (str) =>
    str.sort((a, b) => {
        const aStr = a.split("");
        const bStr = b.split("");

        while (aStr.length && bStr.length) {
            const aCode = aStr.shift().charCodeAt();
            const bCode = bStr.shift().charCodeAt();

            if (aCode === bCode) {
                continue;
            } else {
                return aCode - bCode;
            }
        }
        return aStr.length - bStr.length;
    });

// const makeSumSet = (orders) => {
//     const sumSet = Array.from(new Set(orders.map((el) => el.split('')).flat(Infinity)));
//     return sumSet.sort((a,b) => {
//         const aCode = a.charCodeAt();
//         const bCode = b.charCodeAt();
//         return aCode - bCode;
//     });
// }

const isCommonSet = (main, sub) => {
    for (const cur of sub) {
        if (!main.includes(cur)) {
            return false;
        }
    }
    return true;
};

const getCombination = (arr, num) => {
    const answer = [];

    if (num === 1) {
        return arr.map((e) => [e]);
    }

    arr.forEach((el, i, arr) => {
        const rest = arr.slice(i + 1);
        const combinations = getCombination(rest, num - 1);
        const combinationArr = combinations.map((el2) => [el, ...el2]);
        if (combinationArr.length) {
            for (let i = 0; i < combinationArr.length; i++) {
                if (i > 11000) {
                    return answer;
                }
                answer.push(combinationArr[i]);
            }
        }
    });
    return answer;
};

const solution = (orders, course) => {
    // 1. 단품 메뉴들의 합집합을 만든다.
    // const allMenues = makeSumSet(orders);
    // 내가 틀린 가장 큰 문제 중 하나로, 합집합을 해버리면, 조합 때 엄청난 크기의 배열이 된다

    // 2. 단품 메뉴들의 조합을 만든다.
    // const combinations = [];
    // course.forEach((el) => {
    //     combinations.push(...getCombination(allMenues, el));
    // })

    const temp = [];
    course.forEach((el) => {
        for (const order of orders) {
            temp.push(...getCombination(sortString(order.split("")), el));
        }
    });
    // const ans = Array.from(new Set(temp.map((el) => el.join(''))));
    // console.log(ans);
    // console.log(Array.from(new Set(temp)));
    const combinations = Array.from(new Set(temp.map((el) => el.join("")))).map(
        (el) => el.split("")
    );

    // 3. 조합들과 손님의 주문을 각각 비교하여 횟수를 구한다.
    const orderArr = [];
    const result = new Array(orders.length + 1).fill(0);
    for (let i = 0; i < result.length; i++) {
        result[i] = [];
    }
    orders.forEach((order) => orderArr.push(order.split("")));

    combinations.forEach((combination, index) => {
        let count = 0;
        for (const order of orderArr) {
            if (isCommonSet(order, combination)) {
                count++;
            }
        }
        if (count) {
            result[count].push(combination.join(""));
        }
    });

    // 4. 각 팔린 횟수 중에서 가장 메뉴 가짓수length가 큰 것을 뽑는다.
    const dishes = [];
    course.forEach((num) => {
        for (let i = result.length - 1; i >= 2; i--) {
            const max = result[i].filter((el) => el.length === num);
            if (max.length) {
                dishes.push(max);
                break;
            }
        }
    });

    return sortString(dishes.flat(Infinity));
};

나는 난이도가 상당하다고 느꼈다. 내가 생각한 방식은 주석으로 모두 나타냈다.
개인적으로 문제 자체가 설명을 잘 못한다고 느꼈다.
풀고 나니 문제가 다른 의미라는 걸 두세 번 정도 깨달아가며 겨우 풀었다.

반응형