nlog求最长公共子序列

其实关键在于模型的转化,对于两个数列a,b,我们可以用一个O(n^2)的枚举求出其最长公共子序列,但在一些题中就显然不满足了,所以有了nlog的算法。

首先我们要找到a序列中每个元素在b序列中的位置,这个位置可能有多个,我们将它从大到小排列,对于b中不存在的元素这个位置显然是空集,这样我们就得到了a中每个元素在b中的位置也就是多个集合,且集合内部单调递减,然后按照a中元素的先后顺序,将得到的这多个集合合并在一起,我们可以得到一个新的数列,在这个数列中求最长上升子序列就可以。

算法的正确性很显然,对于新得到的数列,每一个元素都代表了a与b有共同元素,而上升则保证了序列的顺序从前到后。

这里有关键一点,在找a序列在b中位置存储时,要从大到小,这样就放止了对于a中一个元素匹配到b中多个元素的可能性。

洛谷模板题:https://www.luogu.org/problem/P1439

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <vector>
 6 using namespace std;
 7 #define N 100005
 8 int read() {
 9     int s=0,f=1;
10     char ch=getchar();
11     for( ; ch<'0'||ch>'9'; f=(ch=='-')?-1:f,ch=getchar()) ;
12     for( ; ch>='0'&&ch<='9'; s=s*10+(ch^48),ch=getchar()) ;
13     return s*f;
14 }
15 vector<int> q[N];
16 int n,a[N],b[N],Low[N],ans=1,Now[N];
17 int find(int Now,int RR) {
18     int L=0,R=RR,mid;
19     while(L<R) {
20         mid=(L+R)>>1;
21         if(Low[mid]>Now) R=mid;
22         else L=mid+1; 
23     } return L;
24 }
25 int main() {
26     n=read();
27     for(int i=1; i<=n; ++i) a[i]=read();
28     for(int i=1; i<=n; ++i) b[i]=read();
29     for(int i=n; i; --i) q[b[i]].push_back(i);
30     for(int i=1; i<=n; ++i) {
31         if(q[a[i]].empty()) continue;
32         for(int j=0; j<q[a[i]].size(); ++j) Now[++Now[0]]=q[a[i]][j];
33     } Low[ans]=Now[1];
34     for(int i=2; i<=Now[0]; ++i) {
35         if(Now[i]>Low[ans]) Low[++ans]=Now[i];
36         else Low[find(Now[i],ans)]=Now[i];
37     } cout<<ans;
38     return 0;
39 }
View Code

 

posted @ 2019-10-13 16:04  Forever_goodboy  阅读(266)  评论(0编辑  收藏  举报