题目描述

给出1-n的两个排列P1和P2,求它们的最长公共子序列。

输入

第一行是一个数n,

接下来两行,每行为n个数,为自然数1-n的一个排列。

输出

一个数,即最长公共子序列的长度

输入样例

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

3

说明

对于50%的数据,n≤1000

对于100%的数据,n≤100000

思路

常见的LCS问题是通过O(n2)的DP解决的,显然此题的数据是过不去的

如何想办法?

这里就要参考在特殊条件下LCS与LIS(最长上升序列)的转换

我们记录下第一个排列每一个数字出现的位置,即Hash[ ai ] = i

而这个序列再按第二个排列排序,即新型成的序列Seq[ i ] = Hash[ bi ]

这样做了以后发现有什么规律呢?

新的序列Seq是满足B排列的先后顺序单调递增的,而在Seq的Hash值是按A排列的先后顺序单调递增的

那么在Seq中找到的最长上升序列就是同时满足A序列和B序列的先后关系的一个子序列

这样就把LCS成功的转换成了LIS问题

我们知道LIS的问题是可以O(n log n) 的解决的,在此不作赘述。

那么我们前面提到的特殊条件是什么呢?

由于这里记录的是Hash值,所以不能有重复的数字。

代码

 #include<set>
#include<map>
#include<stack>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define RG register int
#define rep(i,a,b) for(RG i=a;i<=b;i++)
#define per(i,a,b) for(RG i=a;i>=b;i--)
#define ll long long
#define inf (1<<30)
#define maxn 100005
using namespace std;
int n,cnt;
int hash[maxn],seq[maxn],dp[maxn];
inline int read()
{
int x=,f=;char c=getchar();
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} void solve()
{
int l,r,mx=,mid,p;
rep(i,,n)
{
l=,r=mx,p=;
while(l<=r)
{
mid=(l+r)>>;
if(dp[mid]<=seq[i]) p=mid,l=mid+;
else r=mid-;
}
if(p==mx) dp[++mx]=seq[i];
else dp[p+]=min(dp[p+],seq[i]);
}
cout<<mx;
} int main()
{
int tmp;
n=read();
rep(i,,n) tmp=read(),hash[tmp]=i;
rep(i,,n) tmp=read(),seq[++cnt]=hash[tmp];//如果两个序列的值有不相同的,请在逗号处加上 if(hash[tmp]),此题因为是排列所以不用加
solve();
return ;
}