LIS LCS 映射
题目描述
给出1-n的两个排列P1和P2,求它们的最长公共子序列。
输入输出格式
输入格式:
第一行是一个数n,
接下来两行,每行为n个数,为自然数1-n的一个排列。
输出格式:
一个数,即最长公共子序列的长度
输入输出样例
输入样例#1:
5
3 2 1 4 5
1 2 3 4 5
输出样例#1:
3
说明
【数据规模】
对于50%的数据,n≤1000
对于100%的数据,n≤100000
无法相信这是模板题QAQ
看此题的数据范围,显然使用最长公共子序列的一般DP算法(时间复杂度为O(N^2))肯定会超时,所以我们需要想别的方法。
考虑此题的另一个条件,两个序列都为1…n的一个排列,考虑特殊情况:
如果其中一个排列为(1,2,…,n),另一个排列为(a1,a2,…,an),那么对于两者的任意公共子序列(a[b1],a[b2],…,a[bk]),必有a[b1] < a[b2] < …< a[bk],则两序列的最长公共子序列为排列(a1,a2,…,an)的最长上升子序列。
那么,对于两个一般的排列(a1,a2,…,an)和(b1,b2,…,bn)的最长公共子序列怎么求?
我们定义映射f(ai)=i,那么两个排列可以转换为(f(a1),f(a2),…,f(an))=(1,2,…,n)和(f(b1),f(b2),…,f(bn)),我们进行这样的转换之后,就把本题转换为求最长上升子序列的长度的题目了。
之后便可以用LIS的nlogn算法计算了。
总的时间复杂度为处理映射O(N),求LIS长度为O(N log N),总的时间复杂度为O(N log N)
code:
#include<cstdio>
int n,top;
int a[100005],dp[100005],f[100005];
int search(int x){
int l=1,r=top,mid;
while(l<r){
mid=(l+r)>>1;
if(dp[mid]<x) l=mid+1;
else r=mid;
}
return l;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
f[a[i]]=i;
}
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
a[i]=f[a[i]];
}
for(int i=1;i<=n;i++){
if(dp[top]<=a[i]) dp[++top]=a[i];
else dp[search(a[i])]=a[i];
}
printf("%d",top);
return 0;
}