题目描述
给你两个长度为 (n) 的排列 (P_1) 和 (P_2),这两个排列只包含 ([1,n]) 范围内的整数各一个,求这两个排列的LCS(即:最长公共子序列)的长度。
输入格式
输入的第一行包含一个整数 (n(1 le n le 10^5)),用于表示排列的长度。
输入的第二行包含 (n) 个整数,两两之间以一个空格分隔,用于表示排列 (P_1)。
输入的第三行包含 (n) 个整数,两两之间以一个空格分隔,用于表示排列 (P_2)。
输出格式
输出一个整数,用于表示 (P_1) 和 (P_2) 的 LCS 长度。
样例输入
5
1 2 3 4 5
2 5 3 1 4
样例输出
3
说明/提示
样例说明:
样例中,(P_1) 和 (P_2) 的 LCS 为 ([2, 3, 4])。
暴力解法
暴力解法时间复杂度 (O(n^2)),空间可以优化到 (O(2n)),但是仍然会超时,实现代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int f[2][maxn], n, a[maxn], b[maxn];
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i <= n; i ++) cin >> b[i];
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
if (a[i] == b[j]) f[i%2][j] = f[(i-1)%2][j-1] + 1;
else f[i%2][j] = max(f[(i-1)%2][j], f[i%2][j-1]);
}
}
cout << f[n%2][n] << endl;
return 0;
}
稀疏性质+线段树优化
由于刚才的分析中,如果 (P_1[i] e P_2[j]),则 (d[i,j]) 不能转移到 (d[i+1,j+1]),因为目标函数不能增加。这样,我们应该只关心满足 (P_1[i] = P_2[j]) 的状态 (d[i,j]),也就是说,可以把状态转移方程写成:
显然对于任意的 (i),满足 (P_1[i] = P_2[j]) 的 (j) 有且仅有一个,令 (pair(i)) 为这个唯一的值,则一共只有 (n) 个需要考虑的状态,即 (d[i, pair(i)]),那么边界条件是 (d[n, pair(n)] = 1)。按照 (i=n-1,n-2, cdots, i) 的顺序来计算,则只要把目前计算出来的所有状态 (d[i', pair(i')])(它们自然满足 i' gt i)用合理的方式组织起来,可以很快地从其中找到满足 (pair(i') gt pair(i)) 的最大状态即可。
基于上述思路,使用线段树来存储 (pair(i)) 和对应的 (d[i, pair(i)]) 信息。
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, a[maxn], b[maxn], pos[maxn], ans; // pos[b[i]] = i,因为pair在C++中有特殊用法,所以这里用pos[i]来表示pair(i)
int f[maxn<<2];
void push_up(int rt) {
f[rt] = max(f[rt<<1], f[rt<<1|1]);
}
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
void update(int p, int v, int l, int r, int rt) {
if (l == r) f[rt] = v;
else {
int mid = (l + r)/2;
if (p <= mid) update(p, v, lson);
else update(p, v, rson);
push_up(rt);
}
}
int query(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) return f[rt];
int mid = (l + r) / 2, res = 0;
if (L <= mid) res = max(res, query(L, R, lson));
if (R > mid) res = max(res, query(L, R, rson));
return res;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i <= n; i ++) {
cin >> b[i];
pos[b[i]] = i;
}
for (int i = 1; i <= n; i ++) {
int j = pos[a[i]];
int tmp = 1;
if (i != 1 && j != 1) tmp = max(tmp, query(1, j-1, 1, n, 1)+1);
update(j, tmp, 1, n, 1);
ans = max(ans, tmp);
}
cout << ans << endl;
return 0;
}