【题目描述】
定义合法的括号序列如下:
(1)空序列是一个合法的序列;
(2)如果S是合法的序列,则(S)和[S]也是合法的序列;
(3)如果A和B是合法的序列,则AB也是合法的序列;
下面的都是合法的括号序列:
()、[]、(())、([])、()[]、()[()];
下面的都是非法的括号序列:
(、[、)、)(、([)]、([(];
给定一个由(、)、[、]组成的序列,找出以该序列为子序列的最短合法序列。
【输入描述】
一行字符S(0 <= S <= 100)。
【输出描述】
一行字符,表示最短合法序列。
【样例输入】
([)]
【样例输出】
()[()]
源代码: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,f[101][101]; char S[101]; bool Match(char t1,char t2) //匹配判断。 { return (t1=='('&&t2==')')||(t1=='['&&t2==']'); } void DP() //动态规划。 { for (int a=0;a<n;a++) { f[a+1][a]=0; f[a][a]=1; } for (int a=n-2;a>=0;a--) //从(n-2)才能将S分成两部分。 for (int b=a+1;b<n;b++) //从当前节点到末尾。 { f[a][b]=n; //符合实际的极大值。 if (Match(S[a],S[b])) f[a][b]=min(f[a][b],f[a+1][b-1]); //Situation 1。 for (int c=a;c<b;c++) f[a][b]=min(f[a][b],f[a][c]+f[c+1][b]); //Situation 2。 } } void Print(int t1,int t2) //输出。 { if (t1>t2) //数据不符合实际。 return; if (t1==t2) //数据为单个字符。 { if (S[t1]=='('||S[t1]==')') printf("()"); else printf("[]"); return; } int ans=f[t1][t2]; if (Match(S[t1],S[t2])&&ans==f[t1+1][t2-1]) //Situation 1。 { printf("%c",S[t1]); Print(t1+1,t2-1); //递归搜索。 printf("%c",S[t2]); return; } for (int a=t1;a<t2;a++) //Situation 2。 if (ans==f[t1][a]+f[a+1][t2]) { Print(t1,a); Print(a+1,t2); return; } } int main() { gets(S); //可能含有空串,故scanf()不可行。 n=strlen(S); DP(); Print(0,n-1); //一步一步地穿越千山万水,回到起源的大地。 printf(" "); //我的 不可能这么坑爹! } /* 新奇的动归题目,输出也繁琐。 设f[i][j]表示字符串S[i~j]至少需要添加的括号数,状态转移无非为以下两种情况: ①对应字符串S的f[]等于"[S]"或"(S)"对应的f[]。 ②字符串S(S>=2)可划分为A、B两个子串,A、B所对应的f[]之和等于S对应的f[]。 综上得状态转移方程: f[i][j]=min(f[i+1][j-1],f[i][k]+f[k+1][j](i<=k<j))。 总结教训: 看到一个题目最好先冷静分析一下,不必在意时间长短,脑子要灵活,不要觉得这道题好像用什么方法做就得用什么方法做。 */