题解 P1439 【【模板】最长公共子序列】

· · 题解

二分法求最长上升子序列这种东东大家难道没见过嘛= =

先简单讲下怎么O(nlogn)搞一个最长上升子序列吧。

考虑一个数列5 2 3 1 4

首先,把5加入答案序列中,然后加2,发现2<5所以显然2替换5不会使结果更差,

那么答案序列就是{2},然后加3,发现3>2,所以直接把3加到答案序列中:{2,3}

然后加1,我们发现1<3,于是我们找到一个最小的但是比1大的数字2,然后把1替换2,为什么这么做不会影响结果呢?你可以这么想,我们当前已经求出了一个当前最优的序列,如果我们用1替换2,然后后面来一个数字替换了3,那么我们就可以得到一个更优的序列,而如果没有数字替换3,那么这个1替换2也就是没有贡献的,不会影响我们结果的最优性。至于,如何找到一个最小的但是大于某个数字的数字,弄个二分查找就行了,因为我们的答案序列是有序的呀。

然后考虑如何解决这道题。

首先,我们可以想到,最长公共子序列,就是两段所含数字完全一样,并且数字的顺序也是完全一样的序列。

而顺序,我们可以想到类似哈希的思想,考虑建立一个类似map的关系数组f[ai]=i,那么我们找到的序列只要是上升的,顺序就是一样的,然后考虑数字完全一样,由于我们已经有了一个f[ai]=i,所以我们把对应的bi改成f[bi],就可以确保数字相等了呀!

这时,就是在f[bi]的数组中求个最长上升子序列了,二分搞一搞就好了。STL大法好!

参考代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=101000;
int b[N],idx[N],n;
int read(){
    int x=0,f=1;char ch=getchar();
    while (ch<'0' || ch>'9'){if (ch=='-')f=-1;ch=getchar();}
    while ('0'<=ch && ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*f;
}
int main(){
    n=read();
    memset(b,0x3f,sizeof(b));
    for (int i=1;i<=n;i++)
        idx[read()]=i;
    for (int i=1;i<=n;i++){
        int x=idx[read()];
        *lower_bound(b+1,b+n+1,x)=x;
    }
    printf("%d",lower_bound(b+1,b+n+1,b[0])-b-1);
    return 0;
}