洛谷 P1439 【模板】最长公共子序列

LIS LCS 映射

题目描述

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

输入输出格式

输入格式:
第一行是一个数n,

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

输出格式:
一个数,即最长公共子序列的长度

输入输出样例

输入样例#1:
5
3 2 1 4 5
1 2 3 4 5
输出样例#1:
3
说明

【数据规模】

对于50%的数据,n≤1000

对于100%的数据,n≤100000

无法相信这是模板题QAQ
看此题的数据范围,显然使用最长公共子序列的一般DP算法(时间复杂度为O(N^2))肯定会超时,所以我们需要想别的方法。

考虑此题的另一个条件,两个序列都为1…n的一个排列,考虑特殊情况:

如果其中一个排列为(1,2,…,n),另一个排列为(a1,a2,…,an),那么对于两者的任意公共子序列(a[b1],a[b2],…,a[bk]),必有a[b1] < a[b2] < …< a[bk],则两序列的最长公共子序列为排列(a1,a2,…,an)的最长上升子序列。

那么,对于两个一般的排列(a1,a2,…,an)和(b1,b2,…,bn)的最长公共子序列怎么求?

我们定义映射f(ai)=i,那么两个排列可以转换为(f(a1),f(a2),…,f(an))=(1,2,…,n)和(f(b1),f(b2),…,f(bn)),我们进行这样的转换之后,就把本题转换为求最长上升子序列的长度的题目了。
之后便可以用LIS的nlogn算法计算了。
总的时间复杂度为处理映射O(N),求LIS长度为O(N log N),总的时间复杂度为O(N log N)

code:

#include<cstdio>

int n,top;
int a[100005],dp[100005],f[100005];

int search(int x){
    int l=1,r=top,mid;
    while(l<r){
        mid=(l+r)>>1;
        if(dp[mid]<x) l=mid+1;
        else r=mid; 
    }
    return l;
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        f[a[i]]=i;
    } 
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        a[i]=f[a[i]];
    }
    for(int i=1;i<=n;i++){
        if(dp[top]<=a[i]) dp[++top]=a[i];
        else dp[search(a[i])]=a[i];
    }
    printf("%d",top);
    return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。 博主:https://www.cnblogs.com/Menteur-Hxy/
原文地址:https://www.cnblogs.com/Menteur-Hxy/p/9248039.html