题目链接
http://poj.org/problem?id=2955
题目大意
我们定义一个字符串序列为“规则的括号序列”当且仅当它满足如下条件:
1、空字符串是规则的括号序列;
2、如果字符串 s 是一个规则的括号序列,那么 (s) 和 [s] 也是规则的括号序列;
3、如果字符串 a 和 b 都是规则的括号序列,那么 ab 也是规则的括号序列;
4、除此之外的字符串都不能称为规则的括号序列。
举个例子,下面的这些字符串都是规则的括号序列:
(), [], (()), ()[], ()[()]
与此同时,下面的这些字符串都不是规则的括号序列:
(, ], )(, ([)], ([(]
给你一个字符串 a1a2...an,你的任务是找到它的最长的一个满足“规则的括号序列”条件的子序列,并输出该最长规则括号子序列的长度。也就是说,你需要找到最长的一组下表i1,i2,...,im,他们满足1≤i1<i2<...<im≤n,同时ai1aid...aim是规则的括号序列。
比如,给你一个字符串 ([([]])] ,它的最长规则的括号子序列是 [([])]。
【输入格式】
输入包含多组样例。每组样例占据一行,包含一个字符串,该字符串仅由 (,),[,]组成。字符串的长度在1到100之间。
输入数据的最后一行包含一个字符串“end”用于标识文件结束。
【输出格式】
对于每一组数据(除了最后的“end”),你需要输出它的最长规则的括号子序列的长度。
【样例输入】
((()))
()()()
([]])
)[)(
([][][)
end
【样例输出】
6
6
4
0
6
解题思路
区间dp。
大佬说是板子题,我连板子都不会。
先枚举区间长度,再枚举区间左端点,计算区间右端点;
转移方程:
由匹配规则3可知,dp[L][R]=max(dp[L][R],dp[L][k]+dp[k+1][R])
(dp[i][j]表示区间[i,j]最多匹配的个数)
由匹配规则2可知,当区间左端点与区间右端点匹配时,dp[L][R]=dp[L+1][R-1]+2
所以,当区间两端点匹配成功时,需要多计算个dp[L][R]=dp[L+1][R-1]+2
(可能还会想到 当左右端点不匹配时 dp[L][R]=max(dp[L+1][R],dp[L][R-1])
,其实这只是第一个转移方程的特殊情况, k=L或R-1)
AC代码
#include<cstring> #include<string> #include<iostream> #include<algorithm> using namespace std; const int N=110; int dp[N][N],n; string str; bool judge(char ch1,char ch2){ if(ch1=='(' && ch2==')') return true; if(ch1=='[' && ch2==']') return true; return false; } int main(){ while(cin>>str&&str!="end"){ memset(dp,0,sizeof dp); int n=str.size(); str="."+str; for(int len=2;len<=n;len++) for(int L=1;L<n;L++){ int R=L+len-1; if(R>n) break; if(judge(str[L],str[R])) dp[L][R]=dp[L+1][R-1]+2; for(int M=L;M<R;M++) dp[L][R]=max(dp[L][R],dp[L][M]+dp[M+1][R]); } cout<<dp[1][n]<<endl; } }