T
: 테스트케이스 개수
L
: 괄호 문자열의 길이
입력은 (
, )
문자로만 이루어진 괄호 문자열이다.
각 테스트케이스에 대해 길이가 L
인 올바른 괄호 문자열 개수를 센 후, 그 수를 1000000007
로 나눈 나머지를 구해야 한다.
⭐️ 올바른 괄호의 기준
- 괄호의 짝이 제대로 이루어진 경우
- 짝이 맞는다면
L
은 무조건 짝수이어야만 함L
이 홀수 → 무조건 올바른 괄호 문자열 0개- 올바른 괄호 문자열끼리 붙여도 올바른 괄호 문자열 ‼️
L
이 짝수인 경우에 한해 가능한 올바른 괄호 문자열을 구하면 다음과 같다.
L=2
→ 1개
()
L=4
→ 2개
()()
(())
L=6
→ 5개
()()()
(())()
()(())
(()())
((()))
L=8
→ 14개
()()()()
(())()()
()(())()
(()())()
((()))()
()()(())
(())(())
()((()))
()(()())
(()()())
((())())
(()(()))
((()()))
(((())))
위에서 언급한 올바른 괄호의 기준처럼 올바른 괄호 문자열은 이전 짝수 단계의 문자열들을 활용해 만들 수 있다.
L이 증가할 때마다 괄호쌍 ()가 하나씩 증가시킨다고 할 때, 올바른 괄호 문자열이 들어갈 수 있는 2가지 위치가 존재한다.
이 2가지 위치에 이전 L에서 구한 올바른 괄호 문자열을 배치하면 될 것이다.
현재 L=8
이라면 2가지 위치에
1. L=6
& L=0
2. L=4
& L=2
3. L=2
& L=4
4. L=0
& L=6
라는 4가지 경우가 발생한다.
(L=0
엔 괄호가 없으므로 올바른 괄호 문자열이라 간주)
각각의 경우에서 발생하는 경우의 수를 계산해서 모두 합한다면 원하는 답을 얻을 수 있을 것이다.
앞의 결과를 이용해 답을 구할 수 있으므로 점화식으로 표현한다면 다음과 같다.
dp[i] = dp[0] * d[i-2] + dp[2] * dp[i-4] + ... + dp[i-2] * dp[0]
크기가 5000이면서 0으로 채워진 dp 테이블을 정의한다.
dp[0] = 1
로 초기화한다.
누적합을 구하기 위해 이중 for문을 통해 점화식을 계산하여 테이블을 채운다.
dp 테이블 채우기 →
최종 시간복잡도
최악의 경우 이 되어, 2초 내에 연산 가능하다.
DP 알고리즘 활용
import sys
input = sys.stdin.readline
# dp 테이블 정의
dp = [0] * 5001
# dp 테이블 초기화
dp[0] = 1
# 점화식으로 짝수에만 dp 테이블 채우기
for n in range(2, 5001, 2):
for i in range(2, n + 1, 2):
dp[n] += dp[i - 2] * dp[n - i]
# 1000000007로 나눈 값 저장
dp[n] %= 1000000007
# 테스트케이스 개수 입력
T = int(input())
# 테스트케이스만큼 반복
for _ in range(T):
# L 입력
L = int(input())
# 결과 출력
print(dp[L])