POJ 2955 Brackets
我们给出了“正则括号”序列的以下归纳定义:
- 空序列是一个 正则方括号序列;
- 如果s是正则方括号序列,那么(s)和[s]是正则括号序列;
- 如果a和b是正则括号序列,则ab是正则括号序列;
- 没有其他序列是正则括号序列。
例如,以下所有字符序列都是正则括号序列:
(),[], (()), ()[], ()[()]
而下列字符序列不是:
(,],)(,([)], ([(]
思路
状态表示:
\(f(i,j)\):字符串\(s\)的子区间\([i,j]\)的最长正则括号子序列的长度。
状态转移:
- 如果字符串\(s\)是空串,或长度为\(1\),那么最长正则括号子序列肯定是\(0\);
- 如果字符串\(s\)的长度为2, 那么当\(s=="()"\)或者\(s=="[]"\)的时候,最长正则括号子序列的长度为\(2\);否则长度为\(0\)。
- 当 \(s[l]=='('\) 并且 \(s[r]==')'\)或者\(s[l]=='['\) 并且 \(s[r]==']'\) 时,有如下转移方程:
\[f(l,r)=f(l+1,r-1)+2
\]
- 如果不是\((s')\)或者\([s']\)的情况,我们可能会想到如下的转移方程:
\[f(l,r)=\max(f(l+1,r),f(l,r-1))\tag {1}
\]
但最长正则括号子序列可能是由若干正则子序列拼接而成的,如\(s="()()"\),这时我们需要去枚举\(k \in [l+1,r-1]\):
\[f(i,j)=\max(f(i,j),f(i,k)+f(k+1,j))\tag {2}
\]
可以发现,\((1)\)是\(k\)为\(l\)和\(r-1\)时的特殊情况,结合\((1)(2)\)可得:
\[f(i,j)=\max(f(i,j),f(i,k)+f(k+1,j))\ \ k \in [l,r-1]
\]
综上:
- 如果\(s\)形如\((s')\)或\([s’]\),转移到\(s[l+1 \sim r-1]\)。
- 如果\(s\)至少有两个字母,则可以分成ab,转移到\(s[l_a\sim r_a]+s[l_b+r_b]\)。
const int N=110;
int f[N][N];
string s;
int n;
bool match(int x,int y)
{
return s[x] == '(' && s[y] == ')' || s[x] == '[' && s[y] == ']';
}
int main()
{
while(cin>>s)
{
if(s == "end") break;
memset(f,0,sizeof f);
n=s.size();
for(int i=n-1;i>=0;i--)
for(int j=i+1;j<n;j++)
{
if(match(i,j)) f[i][j]=f[i+1][j-1]+2;
for(int k=i;k<j;k++)
f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);
}
cout<<f[0][n-1]<<endl;
}
//system("pause");
return 0;
}