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 }