AcWing每日一题 3510.最长公共子序列(DP,二分,nlogn,最长上升子序列)

最长公共子序列

添加链接描述

给出两个长度为 n 的整数序列,求它们的最长公共子序列(LCS)的长度,保证第一个序列中所有元素都不重复。

注意:

第一个序列中的所有元素均不重复。 第二个序列中可能有重复元素。 一个序列中的某些元素可能不在另一个序列中出现。

数据范围 1≤n≤106, 序列内元素取值范围 [1,106]。

输入格式
第一行包含一个整数 n。

接下来两行,每行包含 n 个整数,表示一个整数序列。

输出格式
输出一个整数,表示最长公共子序列的长度。

输入样例1:
5
1 2 3 4 5
1 2 3 4 5
输出样例1:
5
输入样例2:
5
1 2 3 5 4
1 2 3 4 5
输出样例2:
4


最长公共子序列的O(nlogn)做法是将其转化为求最长上升子序列,我们用id[x]来表示第一个序列中的值对应的编号(可以是离散化之后的第一个序列),然后求id[b[i]]的最长上升子序列即可,运用二分时间复杂度可以达到nlogn

求最长上升子序列:
枚举第二个序列n个数,第i个数(即为id[b[i]],与第一个序列对应的编号)时,

第一种情况,目前的上升子序列碰到第i个数后无法继续增加,所以需要找到可以与当前数构成上升子序列的序列,将第i个数作为下一个数,也就是找第一个比 id[b[i]] 大的数,再将这个位置的数替换为id[b[i]]

第二种情况,目前的上升子序列碰到第i个数可以继续增加,那么只要把id[b[i]]作为下一个数即可

然后你会发现,两种情况其实都是一样的,第二种情况中,序列中所有的数一定比id[b[i]]小,同样是找第一个比id[b[i]]大的数

AC代码如下:

//手机双击代码可以全屏看哦
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+3;
int a[maxn],b[maxn];
int id[maxn],s[maxn];
//s为当前上升子序列
int n;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		id[a[i]]=i;	
	}
	for(int i=1;i<=n;++i)
		scanf("%d",&b[i]);
	int ans=0;//ans为最长上升子序列长度
	for(int i=1;i<=n;++i){
		if(id[b[i]]==0)continue;
		//如果该数在a序列中不存在,不考虑
		int L=0,R=ans;
		while(L<=R){//二分查找第一个比id[b[i]]大的数
			//找不到L会一直增大到当前序列长度加一
			int mid=(L+R)>>1;
			if(s[mid]<id[b[i]])L=mid+1;
			else R=mid-1;
		}
		s[L]=id[b[i]];//插入
		ans=max(L,ans);
	}
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值