괄호문자열의 갯수를 세는 문제다. 경우의 수를 세는 문제는 완전탐색류의 문제라고 생각하면 될 것 같다. 그리고 완전탐색은 DP로 해결하는 경우를 생각할 수 있다.
우선 괄호문자열은 쌍으로 존재하므로, 홀수인 경우 무조건 0인 것을 알 수 있다. 그리고 나머지의 경우들을 어떻게 세어야 할까 고민을 해보면 된다.
괄호문자열의 특징을 생각해보면, 무조건 열고 닫혀야 됨을 알 수 있다.
그리고 2개, 4개, 6개 짜리를 생각해보면
맨 처음에 나는 ()
를 하나의 그룹으로 생각하고 최대 문자열의 길이/2
만큼의 그룹을 하나씩 만들어가면서 그 안에 필요한 괄호의 숫자들을 넣는 것을 생각했다.
예를 들어, 6개의 경우
(?)
(?)(?)
(?)(?)(?)
이렇게 1개, 2개, 3개의 그룹을 늘려가면서 ?를 채우면 되는 것으로 생각했다.
(?)
는 ?
안에 4개의 괄호문자열 경우의 수를 넣으면 되고, (?)(?)
는 각각 0, 2개의 괄호문자열 경우의 수 조합을 나열하면 된다.
이러면 경우의 수를 세는 방법이 복잡해지는데, 조금더 생각해보니 맨 앞에 올 수 있는 괄호의 조합을 고정시키고 나머지 문자열의 갯수로 만들 수 있는 경우의 수를 곱하면 해결할 수 있음을 깨달았다.
즉, 6개의 경우
(4)
* 0개로 만들 수 있는 경우의 수(2)
* 2개로 만들 수 있는 경우의 수(0)
* 4개로 만들 수 있는 경우의 수이 생각까지 도달하여 코드를 구현하면 된다.
package main
import (
"bufio"
"os"
"strconv"
)
var (
sc *bufio.Scanner
wr *bufio.Writer
)
func init() {
sc = bufio.NewScanner(os.Stdin)
sc.Split(bufio.ScanWords)
wr = bufio.NewWriter(os.Stdout)
}
func scanInt() int {
sc.Scan()
ret, _ := strconv.Atoi(sc.Text())
return ret
}
const Mod = 1000000007
func main() {
dp := make([]int, 5001)
dp[0] = 1
dp[2] = 1
for l := 4; l <= 5000; l += 2 {
for n := l-2; n >= 0; n -= 2 {
dp[l] += dp[n] * dp[l-n-2]
dp[l] %= Mod
}
}
tc := scanInt()
for tci := 0; tci < tc; tci++ {
l := scanInt()
if l%2 == 0 {
wr.WriteString(strconv.Itoa(dp[l]))
} else {
wr.WriteByte('0')
}
wr.WriteByte('\n')
}
wr.Flush()
}