题意:给定一个由'(' , ')' , '[' , ']'四个字符组成的字符串s,求一个最短的合乎括号规范的字符串t,使得s是t的子序列。合乎规范的串的定义是:1. 空串。 2. 若S合乎规范, 那么(S) 和[S] 都合乎规范。3. 如果A 和 B 都合乎规范,那么AB 合乎规范。
Sample Input
([(]
Sample Output
()[()]
思路:
采用动态规划的思想。用二维数组dp[i][j]表示s[i...j]这个串变为规范的需要增加的最少字符数量。对于一个串s[l,r],分类找到子问题:
1、如果s[l]==s[r],那么dp[i][j] = dp[i+1][j-1];
2、遍历[l,r]的分割点k,求出dp[l,k]+dp[k+1][r],找到这些(包括1里面)中最小的作为dp[i][j]。
终止条件:当l==r时,只能单独匹配这一个字符。
用一个数组f记录dp[i][j]取得最小值时的动作。f数组的含义:
-1:单独匹配
-2:两边匹配
非负数:中间匹配点
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <cstdlib> using namespace std; #define clc(s,t) memset(s,t,sizeof(s)) #define INF 0x3fffffff #define N 105 char s[N],res[N<<1]; int dp[N][N],f[N][N],len;/*f数组的含义 -1:最左边单匹配 -2:两边匹配 非负数:中间匹配点*/ int match(char a,int b){ //判断a和b是否为一对匹配的括号 return (a=='('&&b==')')||(a=='['&&b==']'); } int solve(int l,int r){ //记忆化dp int tmp; if(l>r) return 0; if( dp[l][r] != -1) return dp[l][r]; if(l == r){ //单独字符,只能增加一个相称字符予以匹配 f[l][r] = -1; return dp[l][r] = 1; } dp[l][r] = INF; if(match(s[l],s[r])){ //如果两边的能够匹配 tmp = solve(l+1, r-1); if(tmp<dp[l][r]){ f[l][r] = -2; dp[l][r] = tmp; } } for(int i = l;i<r;i++){ //遍历中间分隔点 tmp = solve(l, i)+solve(i+1, r); if(tmp < dp[l][r]){ dp[l][r] = tmp; f[l][r] = i; } } return dp[l][r]; } char ano(char x){ //返回与输入括号相称的另一半 if(x=='(') return ')'; if(x=='[') return ']'; if(x==')') return '('; return '['; } void print(int x,int y,int l,int r){//在结果串res[x...y]中输出与原来串s[l...r]对应的部分 if(l>r || x>y) return; if(f[l][r] == -1){ //单独匹配 if(s[l] == '(' || s[l] == '['){ res[x] = s[l]; res[x+1] = ano(s[l]); }else{ res[x] = ano(s[l]); res[x+1] = s[l]; } print(x+2, y, l+1, r); }else if(f[l][r] == -2){ //两边匹配 res[x] = s[l]; res[y] = s[r]; print(x+1, y-1, l+1,r-1); }else{ //中间隔开 print(x, x+dp[l][f[l][r]]+f[l][r]-l, l, f[l][r]); print(x+dp[l][f[l][r]]+f[l][r]-l+1, y, f[l][r]+1, r); } } int main(){ int j; clc(dp, -1); clc(f, 0); scanf("%s",s); len = (int)strlen(s); j = solve(0,len-1); print(0,len-1+j,0,len-1); res[len+j] = '\0'; printf("%s\n",res); }