題意:
給你一個只包括大小括號的串,一個合法串的定義如下:
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;
}