题目
题目描述
Let us define a regular brackets sequence in the following way:
- Empty sequence is a regular sequence.
- If S is a regular sequence, then (S) and [S] are both regular sequences.
- If A and B are regular sequences, then AB is a regular sequence.
For example, all of the following sequences of characters are regular brackets sequences:
(), [], (()), ([]), ()[], ()[()]
And all of the following character sequences are not:
(, [, ), )(, ([)], ([(]
Some sequence of characters ‘(‘, ‘)’, ‘[‘, and ‘]’ is given. You are to find the shortest possible regular brackets sequence, that contains the given character sequence as a subsequence. Here, a string a1 a2 … an is called a subsequence of the string b1 b2 … bm, if there exist such indices 1 = i1 < i2 < … < in = m, that aj = bij for all 1 = j = n.
输入
The input file contains at most 100 brackets (characters ‘(‘, ‘)’, ‘[’ and ‘]’) that are situated on a single line without any other characters among them.
输出
Write to the output file a single line that contains some regular brackets sequence that has the minimal possible length and contains the given sequence as a subsequence.
样例输入
([(]
样例输出
()[()]
题目大意
输入一个由小括号或中括号组成的序列,用最少的操作使它变为正则括号表达式。
正则括号表达式:
- 空串是正则括号表达式
- 若S为正则括号表达式,那么(S)或[S]都是正则括号表达式
思路
最优方案计算
区间DP,设S[i]到S[j]添加f[i][j]个括号能变为正则括号表达式。
分两类:
- S[i]与S[j]能匹配:f[i][j]=max(f[i][j],f[i+1][j−1])
- S[i]与S[j]不能匹配:f[i][j]=max(f[i][k]+f[k+1][j]|i≤k<j)
对于第一种,S[i]与S[j]能匹配,自然可以不管它;
对于第二种,不能匹配时,就需要在之间找到一个断点k,分别处理i~k和k+1~j这一段,使它们变成正则括号表达式,那么S[i]和S[j]匹不匹配就无妨了。
注意上面加粗的几个字,当你根据我的分类写出下面的语句时,就WA了,好开心~
if(match(S[i],S[j])) /*...*/
else /*...*/
如果这样,显然可以举出一个反例:
[][]
程序会先判断S[1]和S[4],他们匹配,恩很好,所以f[i][j]=f[i+1][j-1],然后会发现f[i+1][j-1]是2(因为S[2]到S[3]需要加一个’[‘和一个’]’),于是答案是2,正确答案是0。
所以,无论f[i]和f[j]是否能匹配,都需要找一个点分段,执行一遍f[i][j]=max(f[i][k]+f[k+1][j]|i≤k<j),即把之前的else
去掉即可。
显然这个可以用记忆化递归实现,很好想,但是速度不是很快。
用递推的方式,就要赋初值,f[i][i]=1不解释。
最优方案输出
至于输出方案很简单,但是我就不告诉你。
设f[i][j]的“断点”为pre[i][j],即f[i][j]=f[i][pre[i][j]]+f[pre[i][j]+1][j] 。
在for循环找断点时即可得到pre[i][j]。
然后递归print,具体看程序。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 100
char str[MAXN+5];
int Len,f[MAXN+5][MAXN+5],pre[MAXN+5][MAXN+5];
bool match(char x,char y)//括号x和y能否匹配
{
return (x=='('&&y==')')||(x=='['&&y==']');
}
void print(int i,int j)//输出i~j区间方案
{
if(i>j) return;//区间不存在
if(i==j)//长度为1的区间,怎样添加括号是确定的
{
if(str[i]=='('||str[i]==')') printf("()");
else printf("[]");
}
else if(pre[i][j])//说明之前分类的第二种更优
{
print(i,pre[i][j]);//分段
print(pre[i][j]+1,j);
}
else//分类的第一种更优,即S[i]和S[j]在最终的答案中是需要匹配的
//不是能匹配就一定要输出,例如[][],S[1]和S[4]能匹配,但是不是最优
{
printf("%c",str[i]);
print(i+1,j-1);
printf("%c",str[j]);
}
}
int main()
{
scanf("%s",str+1);
Len=strlen(str+1);
for(int i=1;i<=Len;i++)
f[i][i]=1;//对于i~i,肯定要添加1个括号才能匹配
for(int i=Len-1;i>=1;i--)
for(int j=i+1;j<=Len;j++)//区间DP模型
{
f[i][j]=Len+1;//极大值,因为i~j最多可能放Len个括号
//让我很恶心的是之前赋Len就WA了= =
if(match(str[i],str[j]))
f[i][j]=min(f[i][j],f[i+1][j-1]);//能够匹配的情况
for(int k=i;k<j;k++)//不论能否匹配都要找一次,因为有可能有更好的
if(f[i][j]>f[i][k]+f[k+1][j])
{
f[i][j]=f[i][k]+f[k+1][j];
pre[i][j]=k;//存断点
}
}
//printf("%d\n",f[1][Len]);//输出最少添加的括号数量
print(1,Len);puts("");
}