Brackets Sequence
Time Limit: 1000MS | Memory Limit: 65536K | |||
Total Submissions: 35539 | Accepted: 10273 | Special Judge |
Description
Let us define a regular brackets sequence in the following way:
1. Empty sequence is a regular sequence.
2. If S is a regular sequence, then (S) and [S] are both regular sequences.
3. 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.
Input
The input file contains at most 100 brackets (characters '(', ')', '[' and ']') that are situated on a single line without any other characters among them.
Output
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.
Sample Input
([(]
Sample Output
()[()]
题意:给定一个括号序列,通过填补最少个字符,使原输入成为正规化的括号序列。
题解:起初考虑从左至右搜索,对于每一个左括号找到右边第一个未使用过的右括号,但这样找到的答案不满足正规化的定义,例如([)]输出仍为([)],但是不是正规化的括号序列。
由于要求解的问题是,给定一个字符串c1...cn,求出最小需要添加的字符个数(输出可以在求出最小个数后记录添加括号的位置),定义dp[i][j]为补充子串ci...cj所需添加的最小字符数,则状态转移方程为dp[i][j] = min{dp[i][k] + d[k+1][j]},检查后发现漏掉了向字符串两端扩展的定义。即dp[i][j] = dp[i+1][j-1] (c[i]与c[j]是匹配的括号对),因为匹配时并不需要添加字符。
解决了DP难题之一的状态转移方程,另一个就是如何定义循环的问题。
区间DP从小区间扩大到大区间,即区间长度为n的解依赖于长度为(n-1...1)的区间的解,所以循环首先从起点开始,逐步遍历长度为1...n的区间,最终长度为n的区间的解为最优解。回忆一下,区间dp最经典的问题:定义矩阵乘积顺序求解最小计算量。
for (int k = 1;k <= n;k++)
for (int i = 1;i <= n-k;i++)
j = i+k;
dp[i][j] = init();
for (int mid = i+1; mid <= j;mid++)
dp[i][j] = min(dp[i][j], dp[i][mid] + dp[mid+1][j])
对于打印,则通过pos记录额外添加括号的位置,初始pos为-1,表示不需要加括号。转移过程dp[i][j] = min{dp[i][k] + d[k+1][j]}记录下pos[i][j]=k为插入括号的位置。则打印括号序列时,pos[i][j]==-1时打印出c[i],c[j]的括号,并且继续递归打印c[i+1,...j-1]。否则当前位置没有加入括号,递归c[i,...k]和c[k+1,...j]。当i==j时,根据当前字符为(|)|[|],补全括号。
程序:
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>
#include<cstdio>
#define MAXN 100010
using namespace std;
int dp[101][101];
int pos[101][101];
char c[101];
void print(int i, int j){
if (i > j) return;
if (i == j) {
if (c[i] == '(' || c[i] == ')') printf("()");
else printf("[]");
}
else if (pos[i][j] == -1) {
printf("%c", c[i]);
print(i+1, j-1);
printf("%c", c[j]);
}
else {
print(i, pos[i][j]);
print(pos[i][j]+1, j);
}
}
bool match(int i, int j){
if (c[i] == '(' && c[j] == ')') return true;
if (c[i] == '[' && c[j] == ']') return true;
return false;
}
int main(){
while(gets(c) != NULL) {
memset(dp, 0, sizeof(dp));
memset(pos, -1, sizeof(pos));
int len = strlen(c);
for (int i = 0;i < len;i++) {
dp[i][i] = 1;
}
for (int i = 1;i < len;i++){
for (int j = 0;i + j < len;j++){
int k = i + j;
dp[j][k] = MAXN;
if (match(j, k)) {
dp[j][k] = dp[j+1][k-1];
pos[j][k] = -1;
}
for (int r = j;r < k;r++){
if (dp[j][k] > dp[j][r] + dp[r+1][k]) {
dp[j][k] = dp[j][r] + dp[r+1][k];
pos[j][k] = r;
}
}
}
}
print(0, len-1);
printf("\n");
}
return 0;
}