「排列最长公共子序列」P1439 【模板】最长公共子序列
知识点: 线性DP,LCS,LIS
原题面
题目要求:
给定 \(1,2,\dots n\) 的两个排列 \(a,b\),求其最长公共子序列。
\(n\le 10^5\)
分析题意
算法一
有一个极其显然的做法。
设 \(f_{i,j}\) 为,\(a\) 中匹配到第\(i\) 位,\(b\) 中匹配到第 \(j\) 位时,最长公共子序列的长度。
讨论 \(a_i\) 与 \(b_j\) 是否相等,则有:
\(f_{i,j} = \begin{cases}f_{i-1,j-1} +1 & a_i=b_j\\\max ({f_{i,j-1}, f_{i-1,j}}) & \text{otherwise}\end{cases}\)
复杂度 \(O(n^2)\),期望得分 \(50\text{pts}\)。
算法二
上述算法已无法再优化,考虑奇技淫巧。
排列中不存在重复元素,考虑建立一一映射关系。
令 \(pos_{a_i} = i\),使 \(a_i \Rightarrow i, b_i \Rightarrow pos_{b_i}\)。
即发生如下的转化:
\[\begin{aligned}a&:3\ 1\ 5\ 4\ 2\\b&:2\ 3\ 4\ 5\ 1\end{aligned} \Longrightarrow \begin{aligned}&a:1\ 2\ 3\ 4\ 5\\&b:5\ 1\ 4\ 3\ 2\end{aligned}
\]
显然映射后的 LCS 的长度不会受影响。
由于 \(a_i\) 单调递增,则 LCS 也单调递增。
换言之,\(b\) 中所有单调递增子序列,均是一公共子序列。
问题转化为,求 \(b\) 最长的单调递增子序列的长度。
即经典的 LIS 问题。
使用 \(O(n\log n)\) 算法求解即可,期望得分 \(\text{100pts}\)。
代码实现
\(O(n\log n)\ \text{100pts}\)
//
/*
By:Luckyblock
*/
#include <cstdio>
#include <ctype.h>
#include <algorithm>
#define ll long long
const int kMaxn = 1e5 + 10;
//=============================================================
int n, a[kMaxn], b[kMaxn], map[kMaxn];
int lth, f[kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
int Search(int val) {
int l = 0, r = lth;
while (l < r) {
int mid = (l + r) >> 1;
if (f[mid] > val) r = mid;
else l = mid + 1;
}
return l;
}
//=============================================================
int main() {
n = read();
for (int i = 1; i <= n; ++ i) {
a[i] = read(); map[a[i]] = i;
}
for (int i = 1; i <= n; ++ i) b[i] = map[read()];
lth = 1;
f[1] = b[1];
for (int i = 2; i <= n; ++ i) {
if (b[i] > f[lth]) {
f[++ lth] = b[i];
continue;
}
int pos = Search(b[i]);
f[pos] = std :: min(f[pos], b[i]);
}
printf("%d\n", lth);
return 0;
}
\(O(n^2)\ \text{50pts}\)
#include <cstdio>
#include <ctype.h>
#include <algorithm>
#define ll long long
const int kMaxn = 1e3 + 10;
//=============================================================
int n, a[kMaxn], b[kMaxn], f[kMaxn][kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
//=============================================================
int main() {
n = read();
for (int i = 1; i <= n; ++ i) a[i] = read();
for (int i = 1; i <= n; ++ i) b[i] = read();
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
f[i][j] = std :: max(f[i][j - 1], f[i - 1][j]);
if (a[i] == b[j]) f[i][j] = std :: max(f[i][j], f[i - 1][j - 1] + 1);
}
}
printf("%d\n", f[n][n]);
return 0;
}
作者@Luckyblock,转载请声明出处。