最长公共子序列
给出两个长度为 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;
}