括号序列(区间dp)
输入一个长度不超过100的,由"(",")","[",")"组成的序列,请添加尽量少的括号,得到一个规则的括号序列。如有多解,输出任意一个序列即可。
括号序列是这样定义而成的:
- 空序列是括号序列
- 如果S是括号序列,那么(S)和[S]也是正规括号序列
- 如果A和B都是正规括号序列,那么AB也是正规括号序列。
所以,只要一个括号序列不是空序列,我们一定可以把它从两端剥开,或者把它划分成两个小括号序列。设\(f[i][j]\)表示字串\(s[i][j]\)至少要添加几个括号,那么\(f[i][j]\)一定可以转移到\(f[i][k]+f[k][j]\)。如果\(s[i]=s[j]\),那么也可以转移到\(f[i+1][j-1]\)。
由此可见,区间dp的必要条件是划分后解会不同。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=105;
int T, f[maxn][maxn], n;
char s[maxn];
//函数能使语义更清晰
bool match(int x, int y){ return s[y]-s[x]>0&&s[y]-s[x]<=2; }
void print(int l, int r){ //[l,r]
if (l>r) return;
if (l==r){
if (s[l]=='('||s[l]==')') printf("()");
else printf("[]"); return; }
int ans=f[l][r];
if (match(l, r)&&f[l+1][r-1]==ans){
putchar(s[l]); print(l+1, r-1); putchar(s[r]);
return; }
for (int k=l; k<r; ++k)
if (f[l][k]+f[k+1][r]==ans){
print(l, k); print(k+1, r); return; }
}
int main(){
scanf("%d", &T);
while (T--){
fgets(s, maxn, stdin);
fgets(s, maxn, stdin);
n=strlen(s)-1;
for (int i=0; i<n; ++i) f[i+1][i]=0, f[i][i]=1;
for (int i=n-2; i>=0; --i)
for (int j=i+1; j<n; ++j){
f[i][j]=n;
if (match(i, j)) f[i][j]=f[i+1][j-1]; //[i,j]
for (int k=i; k<j; ++k)
f[i][j]=min(f[i][j], f[i][k]+f[k+1][j]);
}
print(0, n-1); puts("");
}
return 0; //忘记return 0了!!
}