经过长时间的摸索和练习,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\]。
对于40%的数据,n, m≤3000 对于100%的数据,n, m≤300000
一行一个整数,表示两个数列的最长公共子序列的长度。
6 6 1 3 5 7 9 8 3 4 5 6 7 8
4
Luogu:https://www.luogu.org/problem/show?pid=3402
二分
这道题的弱化版在这里。
因为数据加强了,我们不能使用弱化版中的动态规划方法了。
题目中给出了元素互不相同的条件。我们可以将求最长公共子序列(LCS)转化为求最长递增子序列(LIS)。
读入A数组的时候,我们定义一个Map[x]表示数x对应的编号,Map[x]=i;这样就相当于把数组A变成了一个递增的序列。
如果我们把B数组里的数都按照A中的规则转置一下,这个题目就变成求B的最长递增子序列了,具体操作如下:
然后在处理B数组的时候(假设我们现在处理数字x),先让x=Map[x]如果此时x==0则说明原来的数字x并没有在数组A中出现过,所以自然也不会成为最长公共子序列的解,直接舍去即可。
这时我们将找到的最长递增子序列放入一个vector(这里用Arr表示),并保证其有序。若x(这个x是用Map转置后的)比vector里所有元素都大(即比Arr[Arr.size()-1]大),则直接将其放到队尾;否则二分查找第一个比x大的元素并替换之。
相信你也发现了,这个算法的核心思路是贪心,我们可以看到,它是让Arr中的元素尽可能的小,以此来求出最长递增子序列,也就可以求出原题的最长公共子序列。
需要注意的是,用这种算法求最长递增子序列只能求出其长度,Arr中的数不一定就是最后要求的最长递增子序列。(为什么呢?仔细想一想)
PS:这个题目真心坑爹,不仅要用上面的这个算法,读入还必须用getchar()读入优化,就连scanf都会超时,真是……
转载于:https://www.cnblogs.com/SYCstudio/p/7137870.html