P1439 两个排列的最长公共子序列
题目描述
给出 1,2,…,n1,2,\ldots,n1,2,…,n 的两个排列 P1P_1P1 和 P2P_2P2 ,求它们的最长公共子序列。
输入格式
第一行是一个数 nnn。
接下来两行,每行为 nnn 个数,为自然数 1,2,…,n1,2,\ldots,n1,2,…,n 的一个排列。
输出格式
一个数,即最长公共子序列的长度。
输入输出样例 #1
输入 #1
5
3 2 1 4 5
1 2 3 4 5
输出 #1
3
说明/提示
- 对于 50%50\%50% 的数据, n≤103n \le 10^3n≤103;
- 对于 100%100\%100% 的数据, n≤105n \le 10^5n≤105。
solution
动态规划
-
设两个序列为 a,b 如果定义 f[i] 为 a, b 中 b[i]为结尾的公共子序列的最大长度,则 f[i]为 a 中在b[i]之前那些数j的最大的f[j] + 1
-
1 定义公式
-
f[i] 以 b[i] 为结尾的最长公共子序列长度
-
-
2 递推关系
-
f[i] = f[j] + 1
-
f[j] 为 a 中,b[i] 左侧的最大的f[k] k = 1...i-1
-
-
3 结果
-
ans = max(f[i])
-
-
4 可以借助树状数组在 log n 时间内求出 max(f[k])
其实如果以 b[i] 在, a中的位置作为x[i]的话,最长公共子序列其实x[i]就是最大递增子序列,同样可以用dp求出
代码
#include "cstdio"
#include "iostream"
#include "vector"
#include "cstring"
#include "queue"
#include "algorithm"
#include "unordered_map"
#include "cmath"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-folding-constant"
using namespace std;
/*
* 题目大意:1-n 的两个排列,求最长公共子序列
* 数据范围: n <= 10^5
*
* 思路:动态规划
* 设两个序列为 a,b 如果定义 f[i] 为 a, b 中 b[i]为结尾的公共子序列的最大长度,则 f[i]为 a 中在b[i]之前那些数j的最大的f[j] + 1
*
* 1 定义公式
* f[i] 以 b[i] 为结尾的最长公共子序列长度
* 2 递推关系
* f[i] = f[j] + 1
* f[j] 为 a 中,b[i] 左侧的最大的f[k] k = 1...i-1
* 3 结果
* ans = max(f[i])
*
* 4 可以借助树状数组在 log n 时间内求出 max(f[k])
*
* 其实如果以 b[i] 在, a中的位置作为x[i]的话,最长公共子序列其实x[i]就是最大递增子序列,同样可以用dp求出
*
*/
const int N = 1e5 + 5;
int n, a[N], b[N], c[N], pos[N];
int low_bit(int x) {
return x & -x;
}
void add(int x, int v) {
while (x <= n) {
c[x] = max(c[x], v);
x += low_bit(x);
}
}
int sum(int x) {
int ans = 0;
while (x > 0) {
ans = max(ans, c[x]);
x -= low_bit(x);
}
return ans;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", a + i), pos[a[i]] = i;
for (int i = 1; i <= n; i++) scanf("%d", b + i);
int ans = 0;
for (int i = 1; i <= n; i++) {
int x = sum(pos[b[i]] - 1);
add(pos[b[i]], x + 1);
ans = max(ans, x + 1);
}
printf("%d\n", ans);
return 0;
}
#pragma clang diagnostic pop