区间dp入门(括号匹配)

题目一:括号合法匹配:

题意:给定一个由()[]四种符号组成的字符串,求其中合法满足合法匹配的最长子串的长度。合法匹配有如下几种形式:()、[]、(合法串)、[合法子串]、合法子串+合法子串+....(如()[][][]()[])。

分析:

dp问题三个重要环节:描述状态、状态转移方程、递推边界。

区间dp的话状态描述一般为二位数组,表示起点和终点。

因为是入门,我们暂且假设已经分析出该题为区间dp,所以状态描述就有了,dp[i][j]表示从i到j最长合法子串的长度。。。

首先我们先来看一下有一个字符的情况,比如结果为0,;两个字符的时候呢,如果这两个字符相同,为0+2,否则,还是0;再看3个字符的时候,即在2个字符的基础上加一个字符,此时的最大值可能为1+2的形式或者2+1的形式(分段);分为四段时,则有可能为1+3、2+2,3+1三种形式,显然我们需要枚举断点,且取多种形式中的最大值作为结果:

dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);其实这样看的话还少了两种形式:0+4和4+0,(可视作一种);

而对于这一形式所需要的dp值dp[i][j]我们是不知道的,所以我们可以先处理这种情况,其值由dp[i+1][j-1]得来,如果区间两端元素相同,上式dp值+2,否则,就等于dp[i+1][j-1](其实上述分段取值时已经包含了两端字符不相等时的情况,所以else可以省去),这样的话状态转移方程也就有了:

1:if(str[i]==str[j])dp[i][j]=dp[i+1][j-1]+2;

2:dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);

第三递推边界的话较容易考虑,但不太好想的地方是断点的范围,因为断点的选择决定了我们应该対哪些初始状态赋值,所以这个地方应该手动模拟一下,防止重复计算或者遗漏状态。

AC代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include<algorithm>
#include <set>
#include <queue>
#include <stack>
#include<vector>
#include<map>
#include<ctime>
#define ll long long
#define INF 0x7fffffff
using namespace std;
ll dp[110][110];
int main()
{
    string str;
    while(cin>>str&&str!="end")
    {
        memset(dp,0,sizeof(dp));
        ll n=str.length();
        for(ll len=1;len<n;++len)//枚举长度
        {
            for(ll j=0;j<=n-2&&j+len<=n-1;++j)
            {
                if(str[j]=='('&&str[j+len]==')'||str[j]=='['&&str[j+len]==']')
                {
                    dp[j][j+len]=dp[j+1][j+len-1]+2;
                }
                for(ll k=j;k<j+len;++k)
                {
                    dp[j][j+len]=max(dp[j][j+len],dp[j][k]+dp[k+1][j+len]);
                }
            }
        }
        cout<<dp[0][n-1]<<endl;
        str.clear();

    }
    return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值