Search

    프로그래머스 Lv.2(JS) - 유사 칸토어 비트열
    2023.01.08 6 min read

    프로그래머스 Lv.2(JS) - 유사 칸토어 비트열

    문제

    유사 칸토어 비트열

    문제 설명

    수학에서 칸토어 집합은 0과 1 사이의 실수로 이루어진 집합으로, [0, 1]부터 시작하여 각 구간을 3등분하여 가운데 구간을 반복적으로 제외하는 방식으로 만들어집니다.

    남아는 칸토어 집합을 조금 변형하여 유사 칸토어 비트열을 만들었습니다. 유사 칸토어 비트열은 다음과 같이 정의됩니다.

    • 0 번째 유사 칸토어 비트열은 “1” 입니다.
    • n(1 ≤ n) 번째 유사 칸토어 비트열은 n - 1 번째 유사 칸토어 비트열에서의 1을 11011로 치환하고 0을 00000로 치환하여 만듭니다.

    남아는 n 번째 유사 칸토어 비트열에서 특정 구간 내의 1의 개수가 몇 개인지 궁금해졌습니다. n과 1의 개수가 몇 개인지 알고 싶은 구간을 나타내는 l, r이 주어졌을 때 그 구간 내의 1의 개수를 return 하도록 solution 함수를 완성해주세요.


    제한사항

    • 1 ≤ n ≤ 20
    • 1 ≤ l, r ≤ 5^n
      • l ≤ r < l + 10,000,000
      • lr은 비트열에서의 인덱스(1-base)이며 폐구간 [l, r]을 나타냅니다.

    입출력 예

    nlrresult
    24178

    정답

    풀이 코드

    function solution(n, l, r) {
      const calcBit = (index) => {
        let exp = 0;
        let result = 0;
    
        while (index > 5 ** (exp + 1)) exp++;
        if (exp === 0) return index < 3 ? index : index - 1;
    
        const quotient = Math.floor(index / 5 ** exp);
        const remainder = quotient + 1 !== 3 ? index % 5 ** exp : 0;
    
        result += quotient >= 3 ? (quotient - 1) * 4 ** exp : quotient * 4 ** exp;
    
        return result + calcBit(remainder);
      };
    
      return calcBit(r) - calcBit(l - 1);
    }

    풀이 설명

    cantor bit

    key point
    • 5n까지의 전체 비트열에서 1의 개수는 4n개입니다.
    • 폐구간 [a, b]는 a와 b를 포함하는 사이 구간을 의미합니다.

    const calcBit = (index) => {
      let exp = 0;
      let result = 0;
    
      while (index > 5 ** (exp + 1)) exp++;
      if (exp === 0) return index < 3 ? index : index - 1;
    };

    calcBit는 0번 인덱스부터 주어진 index까지의 구간에서 1의 개수를 반환하는 재귀 함수입니다.

    • 먼저 index보다 작거나 같은 5n 중 가장 큰 n(=exp)값을 구합니다.
      예를 들어 index가 17이라면 exp는 1입니다.(51 < 17 < 52)

    index가 5 미만일 경우(exp===0)에는 전체 비트열이 11011이므로 아래와 같은 방법으로 처리합니다.

    • index < 3 → 1의 개수는 index와 같습니다.
    • index >= 3 → 3번째는 항상 0이므로 1의 개수는 index-1

    cantor bit2

    const calcBit = (index) => {
      //...
      const quotient = Math.floor(index / 5 ** exp);
      const remainder = quotient + 1 !== 3 ? index % 5 ** exp : 0;
    
      result += quotient >= 3 ? (quotient - 1) * 4 ** exp : quotient * 4 ** exp;
    
      return result + calcBit(remainder);
    };

    quotient는 구간이 5개로 나뉘었을 때 index가 몇 번째 구간에 속하는지 나타냅니다.

    remainder는 아래 두 가지 경우로 나눌 수 있습니다.

    • quotient가 2인 경우: 나머지는 항상 0이 되는 중앙 구간이므로 이후 탐색을 막기 위해 0을 저장합니다.

    • 그 외의 경우: index를 5exp로 나눈 나머지를 저장합니다.

    quotient가 3 이상인 경우, 중앙 구간(3번째)을 포함하므로 quotient - 1에 4exp를 곱합니다. 3 미만의 경우에는 단순히 quotient*4exp만큼 1이 존재합니다.

    해당 값을 result에 누적하고 나머지 구간에 대해서는 calcBit를 재귀 호출합니다.


    function solution(n, l, r) {
      const calcBit = (index) => {
        //...
      };
    
      return calcBit(r) - calcBit(l - 1);
    }

    폐구간 [l, r]은 lr의 인덱스를 모두 포함하므로 0부터 r까지의 1의 개수에서 0부터 l-1까지의 1의 개수를 뺀 결과를 리턴합니다.


    References

    프로그래머스 연습 - 유사 칸토어 비트열

    TAGS

    Algorithm

    알고리즘 문제 풀이

    2023.01.08
    6 min read

    TAGS

    Algorithm