题意:
给你一个只包括大小括号的串,一个合法串的定义如下:
1.空串是合法的;
2.若s为合法的,则 [s] (s) 也为合法的;
3.若 a,b为合法的 , ab 也是合法的。
给你一个长度小于100的串,请加入最少的括号使其合法化。
解法:
区间dp,记录转移路径,类似 poj 2955,不过将dp[i][j]的意义改为使子段变为合法的最少插入数,O(n3)。
要点:
1. 初始化的时候,这题与poj 2955 的状态意义不一样,所以初始化时有效区间为inf(因为要求最小的),无效为0(方便转移时边界的处理);
2.边界处理,dp[i][i] 为边界 (区间长度最短),显然是1,因为一个半括号当且仅当加入一个为最优解;
3.对于区间长度为2时并且两个括号失配时的处理,直接是取中间空隙为中断点,因为这种情况需要插入两个括号,因此不需要特殊处理;
4.根据转移路径递归重构答案即可。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
const int maxn = 105;
const int inf = 1<<29;
int dp[maxn][maxn];
int path[maxn][maxn];
string s;
bool match(char a, char b) {
return (a=='('&&b==')') || (a=='['&&b==']');
}
void print(int l , int r){
if(l > r) return;
if(l == r){
if(s[l] == '[' || s[l] == ']') cout <<"[]";
else cout << "()";
return;
}
if(path[l][r] == -1) {
cout << s[l];
print(l+1, r-1);
cout << s[r];
return;
}
int k = path[l][r];
print(l, k);
print(k+1, r);
}
int main() {
while(getline(cin,s)) {
for(int i = 0 ; s[i] ; i++) {
for(int j = 0 ; s[j] ; j++) {
dp[i][j] = i < j ? inf:0;
}
}
memset(path,-1,sizeof(path));
for(int i = 0 ; s[i] ; i++)
dp[i][i] = 1;
for(int i = 0 ; s[i] ; i++) {
for(int j = i-1 ; j >= 0 ; j--) {
if(match(s[j],s[i])){
dp[j][i] = dp[j+1][i-1];
}
for(int k = j ; k < i ; k++){
if(dp[j][i] > dp[j][k]+dp[k+1][i]){
dp[j][i] = dp[j][k]+dp[k+1][i];
path[j][i] = k;
}
}
}
}
print(0, s.length()-1);
cout << endl;
}
return 0;
}