题目描述
给出1-n的两个排列P1和P2,求它们的最长公共子序列。
输入输出格式
输入格式:
第一行是一个数n,
接下来两行,每行为n个数,为自然数1-n的一个排列。
输出格式:
一个数,即最长公共子序列的长度
输入输出样例
输出样例#1:
3
n^2做法
我们定义一个数组dp[i][j],表示第一个序列的前i项,第二个序列前j项中最长公共子序列
那么可以轻易地得出dp方程:
如果当前a[i]=b[j],dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
否则 dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
但是这个做法很明显只能过50%的点,其他的都会Tle;
nlogn做法
仔细一点,观察题目,可以知道,两个序列中数字是一样的,只不过是顺序变化了
所以我们可以先将a序列中每个位置存起来 打个比方
n=8 a: 8 7 6 5 4 3 2 1
b: 8 6 4 3 1 2 5 7
那么,1到8这些数字在a序列中位置分别为:8,7,6,5,4,3,2,1
那么b序列中每个数在a中的位置是:1,3,5,6,8,7,4,2 设这个数组为wz[]
很显然, 如果在wz数组中间呈上升趋势即为一段公共子序列
那么就可以转换成为求最长不下降子序列
所以就把这道题转换成为一个较为初级的题目
AC代码
#include<bits/stdc++.h> using namespace std; const int N=100002; int number,a[N],b[N],wz[N],f[N],len=0; int read(){ int s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar(); return s*w; } int main(){ number=read(); for(int i=1;i<=number;i++)a[i]=read(),wz[a[i]]=i; for(int i=1;i<=number;i++)b[i]=read(),b[i]=wz[b[i]],f[i]=1e9; f[0]=0; for(int i=1;i<=number;i++){ int l=0,r=len; if(b[i]>f[len])f[++len]=b[i]; else{ while(l<r){ int mid=(l+r)>>1; if(f[mid]>b[i])r=mid; else l=mid+1; } f[l]=min(f[l],b[i]); } } cout<<len; return 0; }