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 6
1 3 5 7 9 8
3 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 }
原文地址:https://www.cnblogs.com/mjtcn/p/7354523.html