【模板】最长公共子序列 解题思路
【Horn Studio】编程专栏:【模板】最长公共子序列 问题
题目描述
给出长度为 nn 的两个排列,排列是1~n数字的随机组合,每个数字只出现一次,求它们的最长公共子序列。
输入
第一行是一个数 nn。
接下来两行,每行为 nn 个数。1≤n≤1051≤n≤105。
接下来两行,每行为 nn 个数。1≤n≤1051≤n≤105。
输出
一个数,即最长公共子序列的长度。
样例输入
5
3 2 1 4 5
1 2 3 4 5
样例输出
3
对于这道题,以为是一道简单的LCS,但是一看数据到达的100000就知道不能用常规的LCS,主要是没有对题目给出的条件充分利用,题目上说给出的两个序列中的数的范围是【1---n】,不能重复,只是第一个序列中的那个数在第二个序列中的位置不一样罢了。所以我们只需要找出来第一个序列中的每个位置得数在第二个序列中的位置就可以了。
为什么呢?
因为我们要求的是两个序列的LCS,所以我们要求出来第一个序列与第二个序列最长相似部分,我们把第一个序列的每个数转化成在第二个序列的位置,到时候只需要求出来最长上升序列就可以(转化成了LIS)
这道题需要使用二分算法,可以使用lower_bound来进行简便计算。
输入a数组与b数组时候,还要为相同位置的map(f)赋值:
for (int i = 1; i <= n; i++) { cin >> a[i]; map[a[i]] = i; } for (int i = 1; i <= n; i++) { cin >> b[i]; f[i] = 1e9; }
每次都要在一个全新的循环中执行二分算法计算。代码就是这样,注释写在代码中了
for (int i = 1; i <= n; i++) { int l = 0, r = len, mid; if (map[b[i]] > f[len]) f[++len] = map[b[i]]; else { while (l < r) { mid = (l + r) / 2; //二分计算 if (f[mid] > map[b[i]]) //如果f的二分值大于map的b【i】值(第i次循环中b【i】),那么将r设定为二分值 r = mid; else //否则在l更改为二分值+1 l = mid + 1; } f[l] = min(map[b[i]], f[l]); } }
这道题不是很难,主要是要抓条件!
这道题的完整代码应该都能独立写出来了,为了一些还不明白怎么写的,我这里就在把程序展现出来吧:
# in cl ud e <iostr eam> #inclu de <cstd io> using name space std; int a[100001], b[1 00001], map [100001], f[100001]; int main() { int n; cin >> n; for (int i = 1; i <= n; i++) { ci >> a[i]; map[a[i]] = i; } for (int i = 1; i <= n; i++) { cin >> b[i]; f[i] = 1e9; } int len = 0; f[1] = 1; for (int i = 1; i <= n; i++) { int l = 0, r = le n , mi d ; if (m ap [b[i]] > f[len]) f[++len] = map[b[i]]; else { while (l < r) { mid = =(l + r) / 2; if (f[mid] > map[b[i]]) r = m d; else l = m i d + 1; } f[l] = m in( m a p[ b[i]], f[l ] ); } } cout << l e n; return 0; }//拒绝伸手党!