排列LCS问题

题目描述

给你两个长度为 \(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] \ne P_2[j]\),则 \(d[i,j]\) 不能转移到 \(d[i+1,j+1]\),因为目标函数不能增加。这样,我们应该只关心满足 \(P_1[i] = P_2[j]\) 的状态 \(d[i,j]\),也就是说,可以把状态转移方程写成:

\[d[i,j] = \max \{ d[i',j'] \} + 1, \text{ } P_1[i] = P_2[j], \text{ } i' \gt i, j' \gt 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;
}

posted @ 2020-06-26 00:09  quanjun  阅读(131)  评论(0编辑  收藏  举报