Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

Python으로 0부터 n까지의 값으로 만들 수 있는 고유한 이진 탐색 트리(BST) 개수 구하기

문제 소개

숫자 n이 하나 주어집니다. 이때 0부터 n-1까지, 즉 [0, n) 범위의 값들을 사용해 만들 수 있는 서로 다른 이진 탐색 트리(Binary Search Tree, BST)의 개수를 구하는 것이 목표입니다. 답이 지나치게 커질 수 있으므로, 최종 결과는 109+7로 나눈 나머지를 반환합니다.

예를 들어 입력이 n = 3이라면, 만들 수 있는 고유한 BST의 개수는 5가 됩니다.

Python으로 0부터 n까지의 값으로 만들 수 있는 고유한 이진 탐색 트리(BST) 개수 구하기

핵심 아이디어: 카탈란 수(Catalan Number)

BST의 구조는 노드에 들어가는 실제 값이 아니라 노드의 개수에 의해서만 결정됩니다. 따라서 n개의 서로 다른 값으로 만들 수 있는 고유한 BST의 개수는 n번째 카탈란 수와 같습니다.

C(n) = (2n)! / ((n+1)! × n!)

팩토리얼 값은 급격히 커지기 때문에 직접 계산할 수 없습니다. 대신 모듈러 값 m = 109+7에 대해 곱셈의 나머지만을 누적하고, 나눗셈이 필요한 부분은 m이 소수라는 성질을 이용한 페르마의 소정리로 처리합니다. 즉, 분모 d에 대해 dm-2 mod m을 곱해주면 d로 나눈 것과 동일한 효과를 얻을 수 있습니다.

풀이 단계

  • m := 109 + 7로 설정합니다.
  • 분자 numer := 1, 분모 denom := n + 1로 초기화합니다.
  • i를 1부터 n까지 반복하며 다음을 수행합니다.
    • numer := numer × (n + i) 를 계산한 뒤 m으로 나눈 나머지를 저장합니다.
    • denom := denom × i 를 계산한 뒤 m으로 나눈 나머지를 저장합니다.
  • 반복이 끝나면 numer := numer × denomm-2 mod m 을 계산해 모듈러 역원을 적용합니다.
  • numer mod m 을 반환합니다.

분모를 n+1부터 시작하는 이유는 카탈란 수 공식의 분모가 (n+1)!이기 때문입니다. 루프가 종료되면 numer에는 (n+1)×(n+2)×…×(2n)이, denom에는 (n+1)!이 누적되므로, 두 값을 나누면 정확히 C(n)이 됩니다.

Python 구현 예제

class Solution:
    def solve(self, n):
        m = 10 ** 9 + 7
        numer = 1
        denom = n + 1
        for i in range(1, n + 1):
            numer *= n + i
            numer %= m
            denom *= i
            denom %= m
        numer *= pow(denom, m - 2, m)
        return numer % m

ob = Solution()
print(ob.solve(4))

입력

4

출력

14

n = 4일 때 네 번째 카탈란 수인 14가 출력되는 것을 확인할 수 있습니다.

복잡도 분석

시간 복잡도는 루프의 n회 반복과 거듭제곱 연산 O(log m)을 합쳐 O(n + log m)입니다. 추가로 사용하는 변수는 상수 개뿐이므로 공간 복잡도는 O(1)입니다.