POJ 2955 Brackets

我们给出了“正则括号”序列的以下归纳定义:

  • 空序列是一个 正则方括号序列;
  • 如果s是正则方括号序列,那么(s)和[s]是正则括号序列;
  • 如果a和b是正则括号序列,则ab是正则括号序列;
  • 没有其他序列是正则括号序列。

例如,以下所有字符序列都是正则括号序列:

(),[], (()), ()[], ()[()]

而下列字符序列不是:

(,],)(,([)], ([(]

思路

状态表示:
\(f(i,j)\):字符串\(s\)的子区间\([i,j]\)的最长正则括号子序列的长度。

状态转移:

  1. 如果字符串\(s\)是空串,或长度为\(1\),那么最长正则括号子序列肯定是\(0\)
  2. 如果字符串\(s\)的长度为2, 那么当\(s=="()"\)或者\(s=="[]"\)的时候,最长正则括号子序列的长度为\(2\);否则长度为\(0\)
  3. \(s[l]=='('\) 并且 \(s[r]==')'\)或者\(s[l]=='['\) 并且 \(s[r]==']'\) 时,有如下转移方程:

\[f(l,r)=f(l+1,r-1)+2 \]

  1. 如果不是\((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;
}
posted @ 2021-04-13 21:10  Dazzling!  阅读(59)  评论(0编辑  收藏  举报