回文序列背景下区间DP问题

今天的算法设计的作业code 中的区间DP大部分都是设计和字符串有关系的区间DP 属于经典模型

下面简单整理一下:

回文子串

输入一个字符串 Str ,输出 Str 里最长回文子串的长度。 回文串:指 aba 、 abba 、 cccbccc 、 aaaa 这种左右对称的字符串。 串的子串:一个串的子串指此(字符)串中**连续**的一部分字符构成的子(字符)串 例如 abc 这个串的子串:空串、 a 、 b 、 c 、 ab 、 bc 、 abc

输出最长回文子串的长度 L 。

["daabaac"]

["5"]

这里分享用区间DP解回文问题的解法:

这里我们的dp数组用的是bool类型~存的是当前i,j是否为回文序列

dp[i][j]  <---- dp[i+1][j-1] &&(a[i]==a[j]) 注意边界1 和2 的时候就可以了

代码如下:

#include<iostream>
using namespace std;
const int N=1e5+10;
string str;
int dp[2010][2010];
int res=1;
int main()
{
	cin>>str;
	int n=str.size();
	for(int len=1;len<=n;len++)
	 for(int i=0;i+len-1<n;i++){
	 	int j=i+len-1;
	 	if(i==j)dp[i][j] =1;
	 	else if((str[i]==str[j])&&(len==2||dp[i+1][j-1])){
	 	  dp[i][j] =1;
		  if(len>res)res=len;	
		  }
		}
	cout<<res;
}

POJ 2955 括号问题

给定只含有中小括号的字符串 问最多匹配的数目

也是区间DP的经典想法,由小区间可以递推出来我们的大区间,f [i][j] 为i-j的最多的匹配的数目

f[i][j] = f[i+1][j-1] +check(i,j)(检查 i j是否匹配)

枚举断点 f[i][j] =f[i][k] +f[k+1][j];  维护最大值

代码如下:

#include<iostream>
#include<cstring>
using namespace std;
int dp[1010][1010];
string str;
bool check(int i,int j)
{
	if(str[i]=='('&&str[j]==')')return 1;
	if(str[i]=='['&&str[j]==']')return 1;
	return 0;
}
int main()
{
	while(cin>>str,str!="end")
	{
		memset(dp,0,sizeof dp);
		int n=str.size();
		for(int len=1;len<=n;len++)
		 for(int i=0;i+len-1<n;i++)
		  {
		  	int j=i+len-1;
		  	dp[i][j]=dp[i+1][j-1]+check(i,j);
		  	for(int k=i;k<j;k++)
		  	 dp[i][j] = max(dp[i][j],dp[k+1][j]+dp[i][k]);
		  }
		cout<<dp[0][n-1]*2<<endl;
	}
}

 

POJ 3280

给定字符串 以及每个其中每个字母删除和插入的代价 求将原来的字符串变成回文串的最小代价

考虑dp[i][j] 为 i-j段变成回文串的最小代价,它是由dp[i+1][j] 和dp[i][j-1] 共同来一起决定的

对于dp[i+1][j] 怎么递推到我们的dp[i][j]呢?容易知道它就是 去掉a[i] 或者在 a[j+1] 加上插入一个a[i]

同里 dp[i][j-1]也是这样操作的~可以发现我们只需要插入和删除的最小的那个值就可以了 我们只需要那个值来进行操作~

注意边界~

代码如下:

#include<iostream>
using namespace std;
string str;
int v[1010];
int dp[2010][2010];
int n,m; 
int main()
{
	cin>>m>>n;
	cin>>str;
	for(int i=1;i<=n;i++)
	 {
	 	char c;
	 	int value1,value2;
	 	cin>>c>>value1>>value2;
	 	v[c-'a'] = min(value1,value2); 
	 }
	 
	for(int len=1;len<=n;len++)
	 for(int i=0;i+len-1<n;i++)
	 {
	 	int j=i+len-1;
	 	if(str[i]==str[j])dp[i][j] = dp[i+1][j-1];
	 	else 
	 	 dp[i][j]= min(dp[i+1][j]+v[str[i]-'a'],dp[i][j-1]+v[str[j]-'a']);
	 }
	 
	 cout<<dp[0][n-1];
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值