大体题意:
括号匹配问题,要求添加尽可能少的括号使得括号匹配 ,其中空括号 是匹配的,括号有[]和()两种!
思路:
dp思想:
令dp[i][j]表示字符串i位置到j位置 最少添加的括号数量!
两种方法 递推和 记忆化搜索思路是一样的,说下整体思路:
如果i和j 这两个位置能匹配的话,那么转移就是ans = min {dp[i+1][j-1]}
然后在类似于最优三角剖分的方法,在中间找一个位置!
使得ans = min{dp[i][k] + dp[k+1][j]}
无论匹配不匹配都要进行第二个转移 ,否则[][]这种情况就转移到了 ][这种情况!!
这样最终会得到 dp每个子字符串所要的括号数量!
然后是打印解!
打印直接递归好了,简单易懂!
如果打印到了边界l == r 那么就要输出一个完整的括号()或者[]
否则 如果 l 和r是匹配的,那么就 递归输出 l+1 到r-1
否则 在找中间位置 满足ans 等于dp[i][j]的,在递归输出!
注意在写递推式时,要注意枚举的方向:
这个也很好理解!
转移是转移dp[i+1][j-1] 第一维向大的方向转移 ,就要从小的位置枚举过来!保证前面的存在!
第二维同理!
不过 递推式的速度是记忆化搜索的五倍左右呢~
详细见代码:
递推式代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100 + 10;
const int inf = 0x3f3f3f3f;
char s[maxn];
int dp[maxn][maxn];
bool ok(char a,char b){
return a == '[' && b == ']' || a == '(' && b == ')';
}
void print(int l,int r){
int ans = dp[l][r];
if (l > r)return;
if (l == r){
if (s[l] == '(' || s[l] == ')')printf("()");
else printf("[]");
return ;
}
if (ok(s[l],s[r]) && ans == dp[l+1][r-1]){
printf("%c",s[l]);
print(l+1,r-1);
printf("%c",s[r]);
return;
}
for (int k = l; k < r; ++k){
if (ans == dp[l][k] + dp[k+1][r]){
print(l,k);
print(k+1,r);
return;
}
}
}
int main(){
int n;
scanf("%d%*c",&n);
int cnt = 0;
while(n--){
gets(s);
gets(s);
int len = strlen(s);
for (int i = 0; i < len; ++i){
dp[i][i] = 1;
dp[i+1][i] = 0;
}
for (int i = len-2; i >= 0; --i){
for (int j = i+1; j < len; ++j){
dp[i][j] = 0x3f3f3f3f;
if (ok(s[i],s[j]))dp[i][j] = min(dp[i][j],dp[i+1][j-1]);
for (int k = i; k < j; ++k){
dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j]);
}
}
}
// printf("%d\n",dp[0][len-1]);
if (cnt++)printf("\n");
print(0,len-1);
puts("");
}
return 0;
}
记忆化搜索式代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100 + 10;
int len;
int dp[maxn][maxn];
char s[maxn];
bool ok(int i,int j){
return s[i] == '(' && s[j] == ')' || s[i] == '[' && s[j] == ']';
}
int dfs(int l,int r){
int& ans = dp[l][r];
if (ans != -1)return ans;
if (l > r)return ans = 0;
if (l == r) return ans = 1;
ans = 0x3f3f3f3f;
if (ok(l,r)){
ans = min(ans,dfs(l+1,r-1));
}
for (int k = l; k < r; ++k){
ans = min(ans,dfs(l,k) + dfs(k+1,r));
}
return ans;
}
void print(int l,int r){
int ans = dp[l][r];
if (l > r)return;
if (l == r){
if (s[l] == '(' || s[l] == ')')printf("()");
else printf("[]");
return ;
}
if (ok(l,r) && ans == dp[l+1][r-1]){
printf("%c",s[l]);
print(l+1,r-1);
printf("%c",s[r]);
return;
}
for (int k = l; k < r; ++k){
if (ans == dp[l][k] + dp[k+1][r]){
print(l,k);
print(k+1,r);
return;
}
}
}
int main(){
int n;
scanf("%d%*c",&n);
int cnt = 0;
while(n--){
gets(s);
gets(s);
memset(dp,-1,sizeof dp);
len = strlen(s);
dfs(0,len-1);
// printf("%d\n",dp[0][len-1]);
if (cnt++)printf("\n");
print(0,len-1);
puts("");
}
return 0;
}