线性结构上的dp——LIS,LCS,LCIS

目录​​​​​​​

在这之前的一些概念:

一、最长上升子序列LIS

LIS模板

二、最长公共子序列LCS

LCS模板

三、LCIS最长公共上升子序列

四、不是很正规严谨的总结


在这之前的一些概念:

 

  1. 字符子串:指的是字符串中连续的n个字符
  2. 字符子序列:指的是字符串中不一定连续但先后顺序一致的n个字符,即可以去掉字符串中的部分字符,但不可改变其前后顺序
  3. 公共子序列:如果序列C既是序列A的子序列,同时也是序列B的子序列,则称它为序列A和序列B的公共子序列
  4. 最长公共子序列:显然,就是公共子序列中最长的
  5. 对于一个长度为 n 的序列,它一共有 2^n 个子序列,有 (2^n – 1) 个非空子序列

一、最长上升子序列LIS

Longest Increasing Subsequence

LIS模板

 

题意:在n个整数中从左到右选出尽量多的整数组成一个上升子序列

例如在1 6 2 3 7 5中,LIS就是1 2 3 5

核心思想:dp[i]代表以i结尾的LIS的长度

#include<iostream>
using namespace std;
 
const int N=1000;	//随便写的大小
int dp[N]; 
int nums[N];
 
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>nums[i];
	}
	
	int ans=0;
	//要求到每个数这里的子序列长度 
	for(int i=0;i<n;i++){
		dp[i]=1;//这样初始化
		for(int j=0;j<i;j++){	//那么就在它前边找,看哪个能连上这个数使子序列更长 
			if(nums[i]>nums[j])//如果能连上 
				dp[i]=max(dp[i],dp[j]+1);	//看连上以j为结尾的子序列会不会更长 
				ans=max(ans,dp[i]);	//更新最长子序列的长度 
		}	
	}
		
	cout<<ans;
	return 0;
}

洛谷没找到合适的,力扣:

300. 最长递增子序列 - 力扣(LeetCode)  

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
      int n=nums.size();
      vector<int> dp(n,0);
        int ans=0;
        for(int i=0;i<n;i++){
            dp[i]=1;
            if(n==1)return 1;
            for(int j=0;j<i;j++){
                if(nums[j]<nums[i])
                   dp[i]=max(dp[i],dp[j]+1);
                ans=max(dp[i],ans);
            }
        }
        return ans;
    }
};

二、最长公共子序列LCS

Longest Common Subsequence

LCS模板

 

最长公共子序列,就是A和B的公共子序列中最长的(LCS可能不唯一)

用L(x,y)表达A序列的前x项和B序列的前y项的LCS

那么现在考虑最后一项Ax和By

(1)Ax=By

那么它们LCS的最后一项一定是这个元素!
L(x,y)=L(x-1,y-1)+1

(2)Ax!=By

设t=L(x,y)最后一项,那么t!=Ax 和 t!=By至少有一个成立(t不能同时等于两个不同的值嘛)

若t!=Ax,那就没有Ax的事

L(x,y)=L(x-1,y) 

同理若t!=By

L(x,y)=L(x,y-1)

但我们事先并不知道t,所以我们就去这两种可能中使L大的那个

所以L(x,y)=max(L(x-1,y) ,L(x,y-1))

现在,获得了递推式,还要初始化

x或y=0时L(x,y)=0

总结:

这里还能例证前边的结论:当LCS(x – 1, y) == LCS(x, y – 1)时,其实走哪个分支都一样,虽然长度时一样的,但是可能对应不同的子序列,所以最长公共子序列并不唯一

 

#include<iostream>
using namespace std;
 
const int N=1000;	//随便写的大小
int dp[N][N]; 
int text1[N],text2[N];
 
int main(){
	int n1,n2;
	cin>>n1;
	for(int i=0;i<n1;i++){
		cin>>text1[i];
	}
	cin>>n2;
	for(int j=0;j<n2;j++){
		cin>>text2[j];
	}
	
	int ans=0;
	//要求到每个数这里的子序列长度 
	for(int i=1;i<=n1;i++){
		for(int j=1;j<=n2;j++){
			if(text1[i-1]==text2[j-1])
				dp[i][j]=dp[i-1][j-1]+1;
			else
				dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			ans=max(ans,dp[i][j]);
		}
	}
		
	cout<<ans;
	return 0;
}

1143. 最长公共子序列 - 力扣(LeetCode)

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        const int M = text1.size();
        const int N = text2.size();
        vector<vector<int>> dp(M + 1, vector<int>(N + 1, 0));
        for (int i = 1; i <= M; ++i) {
            for (int j = 1; j <= N; ++j) {
                if (text1[i - 1] == text2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[M][N];
    }
};

三、LCIS最长公共上升子序列

for (int i=1;i<=n;i++)
{
    int val=0;//val是决策集合S(i,j)中f[i-1][k]的最大值 
    for(int j=1;j<=m;j++)
    {
        //原来的k循环+判断+状态转移 
 
        if (a[i]==b[j]) 
            f[i][j]=val+1;
        else 
            f[i][j]=f[i-1][j];
 
        if (b[j]<a[i]) 
            val=max(val,f[i-1][j]);
 
        //j即将增大为j+1,检查j能否进入新的决策集合 
    }
}

四、不是很正规严谨的总结

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值