时间限制:1 sec
空间限制:256 MB
问题描述
给定两个 1 到 n 的排列 A,B (即长度为 n 的序列,其中 [1,n] 之间的所有数都出现了恰好一次)。
求它们的最长公共子序列长度。
输入格式
第一行一个整数 n ,意义见题目描述。
第二行 n 个用空格隔开的正整数 A[1],⋯,A[n],描述排列 A。
第三行 n 个用空格隔开的正整数 B[1],⋯,B[n],描述排列 B。
输出格式
一行一个整数,表示 A,B 的最长公共子序列的长度。
样例输入
5
1 2 4 3 5
5 2 3 4 1
样例输出
2
样例解释
(2,3) 和 (2,4) 都可以是这两个序列的最长公共子序列。
数据范围
对于 80% 的数据,保证 n<=5,000。
对于 100% 的数据,保证 n<=50,000。
求最长公共子序列一般来说常见的有o(n^2)的算法。今天向大家介绍一种o(nlong)的方法,(不过此方法有一定的局限性,注意读题)
我们从例子出发讲解,此题有以下样例
5
1 2 4 3 5
5 2 3 4 1
我们记在上面的一串数为数组a,下面的为数组b,如果我们将数组a的每个元素改成这个元素对应在数组b中的位置,
a数组会变成:5 2 4 3 1。
观察新的a数组,我们先观察新的a数组的前两项 5和2,这代表,在原a数组前面的1,2项分别出现在b数组的第5项和第2项,
这构成了交叉,说明这两项不能同时成为构成最长公共子序列的元素。
也就是说,出现在新a数组的逆序对,就表示,所对应的两个元素,在原a数组和b数组的位置相反(出现交叉)
推而广之,求新a数组的最长上升子序列,就是问题的解
为了保证问题能在o(nlogn)时间求解,因此我们最长上升子序列也不能用平时常见的算法,而是才用二分法求。
先说步骤再说原理:用一个数组,要求的最长上升子序列第一个元素直接进入数组,接下来,如果当前元素比数组最后一个元素
大,就可以直接进入,如果比数组最后一个元素小,就在数组中用二分查找,找到刚好比它大的元素,做替换。
答案就是最后数组的大小。
下面解释一下为什么这样子可行。
有点懒得写了,这篇博客解释的很清楚https://blog.csdn.net/shuangde800/article/details/7474903。所以就直接借鉴一下啦,嘻嘻
实现代码:
#include <iostream>
#include <vector>
using namespace std;
int st[5004];
int ans = 0;
void push(int x)
{
st[++ans] = x;
}
// ================= 代码实现开始 =================
/* 请在这里定义你需要的全局变量 */
// 计算最长公共子序列的长度
// n:表示两序列长度
// a:描述序列 a(这里需要注意的是,由于 a 的下标从 1 开始,因此 a[0] 的值为-1,你可以忽略它的值,只需知道我们从下标 1 开始存放有效信息即可)
// b:描述序列b(同样地,b[0] 的值为 -1)
// 返回值:最长公共子序列的长度
int LCS(int n, vector<int> a, vector<int> b)
{
int pos[5004];
for( int i=1; i<=n; i++ )
{
pos[b[i]] = i; //b相应的元素出现在b数组的位置
}
for(int i=1; i<=n; i++)
{
a[i] = pos[a[i]];
}
//求a数组的最长上升子序列
for(int i=1; i<=n; i++)
{
if( a[i] > st[ans])
push(a[i]);
else
{
//替换刚好比他大的元素
int lo = 1; //st的lo
int hi = ans+1;int mi;
while( lo < hi)
{
mi = (lo + hi) >> 1;
if( a[i] < st[mi])
hi = mi;
else if( a[i] >= st[mi])
lo = mi+1;
}
st[lo] = a[i];
}
}
return ans;
}
// ================= 代码实现结束 =================
int main() {
int n, tmp;
vector<int> a, b;
scanf("%d", &n);
a.clear();
b.clear();
a.push_back(-1);
b.push_back(-1);
for (int i = 1; i <= n; ++i) {
scanf("%d", &tmp);
a.push_back(tmp);
}
for (int i = 1; i <= n; ++i) {
scanf("%d", &tmp);
b.push_back(tmp);
}
int ans = LCS(n, a, b);
printf("%d\n", ans);
return 0;
}