题目链接:
题目大意:
定义正规的括号序列如下:
1. 空序列是一个正规的括号序列
2. 如果S是一个正规的括号序列, 那么(S) 和[S] 也都是正规的括号序列。
3. 如果A和B是正规的括号序列, 那么AB也是一个正规的括号序列。
现给定一个括号序列A(只包含小括号和中括号,可能为空序列),求一个正规括号序列B,使得A包含于B,而且B的长度是满足条件的序列中最小的。
思路:
我们用dp[i][j]来表示包含子序列s[i…j]的最短正规括号序列需要添加的括号数,则由定义的2和3可知:
如果 s[i]与s[j]匹配,
dp[i][j] = min{dp[i][k] + dp[k+1][j], dp[i+1][j-1]},i <= k < j;
否则,
dp[i][j] = min{dp[i][k] + dp[k+1][j]},i <= k < j;
以上便是状态转移方程,不过题目要求输出结果序列,所以我们需要标记分解的位置,然后递归打印答案。这可以通过一个mark数组来实现:如果序列不可分解为两个子序列,则mark[i][j] = -1,如果在k处分解,则mark[i][j] = k。
注意,输入可能为空,所以gets来读取一行。
算法步骤:
1) 初始化边界条件:
//单个括号,必然需要添加1个括号与其匹配
dp[i][i] = 1;
2) 根据状态转移方程动态规划求解:
状态转移方程中一共有3个变量:i,j,k,由分解的性质可知,长的序列依赖于短的序列,所以我们要保证短的序列先算。我们在最外层循环设置一个变量l,表示序列的长度,2 <= l <= n,则1 <= i <= n-l+1, j = i+l-1,之后对每个i,用k来遍历划分点,i <= k < j。
for (int l = 2; l <= n; l++)
{
for (int i = 1; i <= n-l+1 ; i++)
{
int j = i+l-1;
dp[i][j] = INF;
if (s[i-1]=='('&&s[j-1]==')' || s[i-1]=='['&&s[j-1]==']')
{
if (dp[i+1][j-1] < dp[i][j])
dp[i][j] = dp[i+1][j-1];
}
mark[i][j] = -1;
for (int k = i; k < j; k++)
{
if (dp[i][k]+dp[k+1][j] < dp[i][j])
{
dp[i][j] = dp[i][k] + dp[k+1][j];
mark[i][j] = k;
}
}
}
}
3) 打印结果:
根据递推关系,我们可以用printAns(1, n)递归地打印出我们的结果。对于printAns(i, j)关系如下:
如果 i > j,返回;
如果i = j,打印一对括号,返回;
如果i < j,
如果序列不用划分,则打印s[i],打印printAns(i+1,j-1),再打印s[i]对应的匹配括号;
否则,打印printAns(i,k)和printAns(k+1,j),其中k为划分点。
算法复杂度:
O(n^3)
源程序:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int INF = 99999999;
char s[120];
int dp[120][120];
int mark[120][120];
void printAns(int i, int j)
{
if (i > j) return;
if (i == j)
{
if (s[i-1] == '(' || s[i-1] == ')')
cout<<"()";
else
cout<<"[]";
return;
}
if (mark[i][j] == -1)
{
if (s[i-1] == '(')
{
cout<<'(';
printAns(i+1, j-1);
cout<<')';
}
else
{
cout<<'[';
printAns(i+1, j-1);
cout<<']';
}
}
else
{
printAns(i,mark[i][j]);
printAns(mark[i][j]+1,j);
}
}
int main()
{
gets(s);
int n = strlen(s);
if (!n)
{
cout<<endl;
}
else
{
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
dp[i][i] = 1;
for (int l = 2; l <= n; l++)
{
for (int i = 1; i <= n-l+1 ; i++)
{
int j = i+l-1;
dp[i][j] = INF;
if (s[i-1]=='('&&s[j-1]==')' || s[i-1]=='['&&s[j-1]==']')
{
if (dp[i+1][j-1] < dp[i][j])
dp[i][j] = dp[i+1][j-1];
}
mark[i][j] = -1;
for (int k = i; k < j; k++)
{
if (dp[i][k]+dp[k+1][j] < dp[i][j])
{
dp[i][j] = dp[i][k] + dp[k+1][j];
mark[i][j] = k;
}
}
}
}
printAns(1, n);
cout<<endl;
}
return 0;
}