题目一:括号合法匹配:
题意:给定一个由()[]四种符号组成的字符串,求其中合法满足合法匹配的最长子串的长度。合法匹配有如下几种形式:()、[]、(合法串)、[合法子串]、合法子串+合法子串+....(如()[][][]()[])。
分析:
dp问题三个重要环节:描述状态、状态转移方程、递推边界。
区间dp的话状态描述一般为二位数组,表示起点和终点。
因为是入门,我们暂且假设已经分析出该题为区间dp,所以状态描述就有了,dp[i][j]表示从i到j最长合法子串的长度。。。
首先我们先来看一下有一个字符的情况,比如结果为0,;两个字符的时候呢,如果这两个字符相同,为0+2,否则,还是0;再看3个字符的时候,即在2个字符的基础上加一个字符,此时的最大值可能为1+2的形式或者2+1的形式(分段);分为四段时,则有可能为1+3、2+2,3+1三种形式,显然我们需要枚举断点,且取多种形式中的最大值作为结果:
其实这样看的话还少了两种形式:0+4和4+0,(可视作一种);
而对于这一形式所需要的dp值我们是不知道的,所以我们可以先处理这种情况,其值由得来,如果区间两端元素相同,上式dp值+2,否则,就等于dp[i+1][j-1](其实上述分段取值时已经包含了两端字符不相等时的情况,所以else可以省去),这样的话状态转移方程也就有了:
第三递推边界的话较容易考虑,但不太好想的地方是断点的范围,因为断点的选择决定了我们应该対哪些初始状态赋值,所以这个地方应该手动模拟一下,防止重复计算或者遗漏状态。
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;
}