首页 > 代码库 > P3402 最长公共子序列(nlogn)

P3402 最长公共子序列(nlogn)

P3402 最长公共子序列

题目背景

DJL为了避免成为一只咸鱼,来找Johann学习怎么求最长公共子序列。

题目描述

经过长时间的摸索和练习,DJL终于学会了怎么求LCS。Johann感觉DJL孺子可教,就给他布置了一个课后作业:

给定两个长度分别为n和m的序列,序列中的每个元素都是正整数。保证每个序列中的各个元素互不相同。求这两个序列的最长公共子序列的长度。

DJL最讨厌重复劳动,所以不想做那些做过的题。于是他找你来帮他做作业。

输入输出格式

输入格式:

 

第一行两个整数n和m,表示两个数列的长度。

第二行一行n个整数a_1,a_2,…,a_n,保证1≤a_i≤〖10〗^9。

第三行一行m个整数b_1,b_2,…,b_m,保证1≤b_i≤〖10〗^9。

 

输出格式:

 

一行一个整数,表示两个数列的最长公共子序列的长度。

 

输入输出样例

输入样例#1:
6 61 3 5 7 9 83 4 5 6 7 8
输出样例#1:
4

说明

对于40%的数据,n, m≤3000

对于100%的数据,n, m≤300000

分析

对于n方的算法,这道题显然过不去,并且这道题每个数字只出现一次,可以用一个“nlogn”的算法,(加引号的)这个算法只适用于A和B中每个数出现的次数是一个很小的数,这一点题目刚好满足。

具体呢,就是对于在B序列中的元素x,我们在A中找到x的出现位置(在A中的位置)并按降序写下来。然后B中的所有x都有对应的数字,这些数字就是序列C,对C求最长不下降子序列。得到的答案即为A和B的LCS长度。

举个栗子:A={c,a,b,e,d,a,b},B={a,d,c,a,d,b},C={6,2, 5, 1, 6,2, 5, 7,3}

比如B中的a他在A中出现的位置是2,6,按降序后就是6,2;其他的同理。

因为求最长不下降子序列有(nlogn)的算法(http://www.cnblogs.com/mjtcn/p/7197034.html),所以,这也就是nlogn的算法了,再加上预处理出,

这个题这样差不多就完成了,但是,为了方便用map写的,洛谷上只过了7个点,改读入优化,加上inline就过了,可以用哈希写,比map快多了

code

 1 #include<cstdio> 2 #include<map> 3 #include<algorithm> 4  5 using namespace std; 6 const int MAXN = 300100; 7 int f[MAXN]; 8  9 map<int,int>p;10 inline int read()11 {12     int x = 0;char ch = getchar();13     while (ch<0||ch>9) ch = getchar();14     while (ch>=0&&ch<=9) x = x*10+ch-0,ch = getchar() ;15     return x;16 }17 inline int search(int l,int r,int x)18 {19     while (l<r)20     {21         int mid = (l+r)>>1;22         if (x<=f[mid]) r = mid;23         else l = mid+1;24     }25     return l;26 }27 int main()28 {29     int n = read(),m = read(),len = 0;30     for (int x,i=1; i<=n; ++i) 31         x = read(),p[x] = i;32     for (int a,x,i=1; i<=m; ++i) 33     {34         a = read();35         x = p[a];36         if (!x) continue;37         if (x>f[len]) f[++len] = x;38         else 39         {40             int p = search(1,len,x);41             f[p] = x;42         }43     }44     printf("%d",len);    45     return 0;46 }

 

P3402 最长公共子序列(nlogn)