最长公共子序列-LCS问题 (LCS与LIS在特殊条件下的转换) [洛谷1439]
Posted iBilllee
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最长公共子序列-LCS问题 (LCS与LIS在特殊条件下的转换) [洛谷1439]相关的知识,希望对你有一定的参考价值。
题目描述
给出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值,所以不能有重复的数字。
代码
1 #include<set> 2 #include<map> 3 #include<stack> 4 #include<queue> 5 #include<cstdio> 6 #include<cstring> 7 #include<iostream> 8 #include<algorithm> 9 #define RG register int 10 #define rep(i,a,b) for(RG i=a;i<=b;i++) 11 #define per(i,a,b) for(RG i=a;i>=b;i--) 12 #define ll long long 13 #define inf (1<<30) 14 #define maxn 100005 15 using namespace std; 16 int n,cnt; 17 int hash[maxn],seq[maxn],dp[maxn]; 18 inline int read() 19 { 20 int x=0,f=1;char c=getchar(); 21 while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} 22 while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();} 23 return x*f; 24 } 25 26 void solve() 27 { 28 int l,r,mx=0,mid,p; 29 rep(i,1,n) 30 { 31 l=1,r=mx,p=0; 32 while(l<=r) 33 { 34 mid=(l+r)>>1; 35 if(dp[mid]<=seq[i]) p=mid,l=mid+1; 36 else r=mid-1; 37 } 38 if(p==mx) dp[++mx]=seq[i]; 39 else dp[p+1]=min(dp[p+1],seq[i]); 40 } 41 cout<<mx; 42 } 43 44 int main() 45 { 46 int tmp; 47 n=read(); 48 rep(i,1,n) tmp=read(),hash[tmp]=i; 49 rep(i,1,n) tmp=read(),seq[++cnt]=hash[tmp];//如果两个序列的值有不相同的,请在逗号处加上 if(hash[tmp]),此题因为是排列所以不用加 50 solve(); 51 return 0; 52 }
以上是关于最长公共子序列-LCS问题 (LCS与LIS在特殊条件下的转换) [洛谷1439]的主要内容,如果未能解决你的问题,请参考以下文章