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

题目描述

给出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

 

/*
关于LCS长度等于构造出来的新序列的LIS长度的证明: 

(1) A、B的一个公共子序列对应新序列的一个严格递增子序列
证: 
    假设A、B的某一个公共子序列长度为k,则其公共子序列在A和B中可以写为
    {A1,A2, ..., Ak}
    {B1,B2, ..., Bk}
    
    如此有A1 = B1,A2 = B2, ...., Ak = Bk, 考虑元素Bi在B中的序号P(Bi),则有
    P(B1)< P(B2) < ... < P(Bk)
    注意此严格递增子序列属于新序列的一个子序列,因此得证
 
(2) 新序列的一个严格递增子序列对应A、B的一个公共子序列
证: 
    设新序列的一个严格递增子序列{P1,P2, ..., Pk},
    任意两个相邻的P不可能属于A中同一个元素,因为A中某元素在B中的序号按照降序排列,且P为上升序列。 
    所以每个P均对应于A中不同位置的元素,设为{A1, A2, ..., Ak}。
    且每个P也对应B中唯一的一个元素,假设为{B1,B2, ..., Bk},
    由P的定义可知A1= B1, A2 = B2, ...., Ak = Bk,因此得证。

步骤:
(1) 计算A中每个元素在B中的序号,并构成新序列
(2) 使用LIS的方法计算最长严格递增子序列
(3) 获取最长公共子序列
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;

const int N=100005;

int n;
int a[N],b;
int seq[N],seq_len;
int lis[N],lis_len;
vector<int> vec[N];

int read()
{
    char c=getchar();int num=0;
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num;
}

int main()
{
    n=read();
    for(int i=1;i<=n;++i)
        a[i]=read();
    for(int i=1;i<=n;++i)
    {
        vec[b=read()].push_back(i);
    }
    for(int i=1;i<=n;++i)
        for(int j=vec[a[i]].size()-1;j>=0;--j)
            seq[++seq_len]=vec[a[i]][j];
    lis[++lis_len]=seq[1];
    for(int i=2;i<=seq_len;++i)
    {
        if(seq[i]>lis[lis_len])
            lis[++lis_len]=seq[i];
        else
            lis[lower_bound(lis+1,lis+lis_len+1,seq[i])-lis]=seq[i];
    }
    printf("%d",lis_len);
    return 0;
}

 

posted @ 2018-02-12 14:11  whymhe  阅读(159)  评论(0编辑  收藏  举报