DOJ #200 括号序列
DOJ #200 括号序列 (区间DP)
题目描述
长度为\(n(1\leq n \leq 500)\)的由[
、]
、(
、)
四种字符组成组成的括号序列\(s\),求最长的括号子序列\(t\)的长度。
题解
考虑区间DP,设 \(f[l][r]\) 表示区间 \([l,r]\) 匹配括号子序列的最长长度。
边界条件有:
- 单个字符:\(f[l][l] = 0\)。
- 双个字符 \(f[l][r] = match(s[l],s[r])\),其中\(match(ch_1,ch_2)\)表示两个字符是否匹配。
否则,考虑枚举一个\(k\)转移,\(f[l][r]\)这个问题可以由两个子问题\(f[l][k]\)和\(f[k+1][r]\)来完成。
另外,如果\(match(s[l],s[r])\),那么也能划分成\(f[l+1][r-1]\)这个子问题。
转移方程:
- \(f[l][r] = f[l][k] + f[k+1][r] (l \leq k < r)\)。
- 如果\(match(s[l],s[r])\),则\(f[l][r] = f[l+1][r-1]+2\)。
可以采用记忆化搜索实现: 时间复杂度\(O(n^3)\)。
# include <bits/stdc++.h>
using namespace std;
const int N = 505;
char s[N];
int f[N][N];
int dfs(int l,int r) {
if (r<=l) return f[l][r]=0;
if (r == l+1) {
if (s[l] == '(' && s[r] == ')') return f[l][r]=2;
if (s[l] == '[' && s[r] == ']') return f[l][r]=2;
else return f[l][r]=0;
}
if (f[l][r]!=-1) return f[l][r];
int res = 0;
for (int i=l;i<r;i++) {
res = max(res,dfs(l,i)+dfs(i+1,r));
}
if (s[l] == '(' && s[r] == ')') res=max(res,dfs(l+1,r-1)+2);
if (s[l] == '[' && s[r] == ']') res=max(res,dfs(l+1,r-1)+2);
return f[l][r] = res;
}
int main() {
memset(f,-1,sizeof(f));
int n; cin>>n;
scanf("%s",s+1);
printf("%d\n",dfs(1,n));
return 0;
}