算法笔记11.2~11.6 最大连续子序列和 最长不降子序列(LIS) 最长公共子序列(LCS) 最大回文子串 DAG最长路

版权声明:原创文章,转载请注明出处 https://blog.csdn.net/hza419763578/article/details/88745974

11.2最大连续子序列和

给定一个数字序列A1,A2,...,An,求i,j(1<=i<=j<=n),使得Ai+..+Aj最大,输出这个最大和
样例:
    -2 11 -4 13 -5 -2
    显然11+(-4)+13=20为和最大的选取情况

暴力:O(n^2)     dp:O(n)

A[i]记录序列
记dp[i]为以A[i]结尾的连续序列(将问题划分为子问题,序列划分为子序列,极限自然是长度为1的序列,可以理解为
以A[i]结尾)的最大和,则dp[i]有两种状态(关键)
1.前面最大和为负数或者i==0,此时dp[i]=A[i]  序列长度为1
2.dp[i]=dp[i-1]+A[i]  序列长度不为1

边界:dp[0]=A[0]
状态转移方程: dp[i]=max(dp[i-1]+A[i],A[i]);

#include<iostream>
#include<algorithm>
using namespace std;

const int N=10010;
int A[N],dp[N];

int main(){
	// freopen("input.txt","r",stdin);
	int n;
	while(cin>>n){
		for(int i=0;i<n;i++){
			cin>>A[i];
		}
		dp[0]=A[0];
		for(int i=1;i<n;i++){
			dp[i]=max(A[i],dp[i-1]+A[i]);//直接自己(之前最大为负) 或者+上之前最大
		}
		cout<<*max_element(dp,dp+n)<<endl;
	}
	return 0;
}

11.3 最长不降子序列(LIS)

在一个数字序列中,找到一个最长的子序列(可以不连续),使得这子序列是不降(非递减)的。
eg:A={1,2,3,-1,-2,7,9}
最长不降子序列:1 2 3 7 9  长度为5

在一个数字序列中,找到一个最长的子序列(可以不连续),使得这子序列是不降(非递减)的。
eg:A={1,2,3,-1,-2,7,9}
最长不降子序列:1 2 3 7 9  长度为5

dp[i]是以A[i]结尾的最长不降子序列长度
1.前面有比A[i]小的(或等于)A[j] 则 dp[i]=dp[j]+1  (dp[j]是所有小于j<i且A[j]<=A[i]中的最大值)
2.前面没有比A[i]小的(或等于)A[j] 则dp[i]=1 A[i]自己一个人组成一个序列

dp[i]=max(1,dp[j]+1)
(j=1,2,...i-1,A[j]<A[i])

#include<iostream>
using namespace std;
const int N=100;
int A[N],dp[N];

int main(){
	// freopen("input.txt","r",stdin);
	int n;
	while(cin>>n){
		for(int i=0;i<n;i++){
			cin>>A[i];
		}
		int ans=0;
		for(int i=0;i<n;i++){
			dp[i]=1;
			for(int j=0;j<i;j++){
				if(A[j]<=A[i]&&dp[j]+1>dp[i]){
					dp[i]=dp[j]+1;
				}
			}
			ans=max(ans,dp[i]);//更新最大长度
		}
		cout<<ans<<endl;
	}
	return 0;
}

11.4最长公共子序列(LCS)

给定两个字符串(或数字序列)A和B,求一个字符串,使得这个字符串是A和B的最长公共部分(子序列可以不连续)

A[i] B[i] 存字符串A,B,由于dp[0][j]=dp[i][0]=0 长度为0时的边界  下标都统一从1开始
dp[i][j]表示以A[i]、B[j]结尾的两个子串的最长公共子序列长度
则:
dp[i][j]={
    dp[i-1][j-1],A[i]==B[j] 
    max(dp[i-1][j],dp[i][j-1]),A[i]!=B[j] 
}

注意:对于 dp[i-1][j-1],A[i]==B[j] ,即第一个分支,一定是dp[i-1][j-1]两个下标都要减,不能写成dp[i][j-1]或者dp[i-1][j]否则可能出错   eg:abc  acc i=j=3时  dp[2][2]+1=2  但是dp[3][2]+1=3 重复利用了A[3]导致出错

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=1000;
char A[N],B[N];
int dp[N][N];

int main(){
	// freopen("input4.txt","r",stdin);
	gets(A+1);//下标从1开始
	gets(B+1);
	int lenA=strlen(A+1);//从A[1]开始计算长度
	int lenB=strlen(B+1);//从A[1]开始计算长度
	for(int i=1;i<=lenA;i++){//要加等号 0 1 2 3 长度3 A[1]~A[3]
		dp[i][0]=0;
	}
	for(int j=1;j<=lenB;j++){
		dp[0][j]=0;
	}
	//或者干脆整个数组初始化为0
	//memset(dp,0,sizeof(dp));

	for(int i=1;i<=lenA;i++){
		for(int j=1;j<=lenB;j++){
			if(A[i]==B[j]){//A[i]与B[j]比 不要写成B[j]了
				dp[i][j]=dp[i-1][j-1]+1;
			}else{
				dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			}
		}
	}

	cout<<dp[lenA][lenB]<<endl;
	return 0;
}

11.5最长回文子串

给出一个字符串S,求S的最长回文子串的长度
样例:
    字符串"PATZJUJZTACCBCC"最长回文子串为"ATZJUJZTA",长度为9

dp[i][j]表示A[i~j]子串是否是回文串,是1 否0
dp[i][j]={
    dp[i-1][j-1],A[i]==A[j]
    0,A[i]!=A[j]
}

边界:dp[i][i]=1  dp[i][i+1]=(A[i]==A[i+1)?1:0     

则枚举时长度L可以从3开始

枚举是以子串长度和左边坐标i(初始位置进行的)

边界计算除了所有长度为1和2的子串情况
第1遍动规:计算所有长度为L=3的所有子串情况
第2遍动规:计算所有长度为L=4的所有子串情况
...
第len-2遍动规:计算所有长度为L=len的所有子串情况 //其实就是判断整个字符串是否也是

之所以要这么复杂是因为递归表达式的特殊,计算下一个状态dp[i][j]需要利用dp[i-1][j-1]这个长度比其小2的子串
L=3之前0要知道
L=4之前2要知道
...
L=len之前len=len-2要知道

#include<iostream>
#include<cstring>
using namespace std;

const int N=1000;
char S[N];
int dp[N][N];

int main(){
	freopen("input5.txt","r",stdin);
	int len,ans;
	int s,e;//辅助 自己加的
	while(gets(S)!=NULL){
		len=strlen(S);
		ans=1;//最大回文子串长度 初值为1
		memset(dp,0,sizeof(dp));
		//边界
		for(int i=0;i<len;i++){
			dp[i][i]=1;	
			if(S[i]==S[i+1]){
				dp[i][i+1]=1;
				ans=2;
			}//否则默认0  dp[i][i+1]不回文
		}

		//动规
		for(int L=3;L<=len;L++){
			for(int i=0;i+L-1<len;i++){//右边界小于len
				int j=i+L-1;
				if(S[i]==S[j]){
					dp[i][j]=dp[i+1][j-1];
					ans=L;//更新最长回文子串长度
					s=i,e=j;
				}//否则不是回文串 默认值0表false
			}
		}

		cout<<ans<<endl;
		S[e+1]='\0';cout<<S+s<<endl;
	}

	return 0;
}

11.6 DAG最长路

猜你喜欢

转载自blog.csdn.net/hza419763578/article/details/88745974
今日推荐