线性DP—洛—两个排列的最长公共子序列

P1439 两个排列的最长公共子序列

题目描述

给出 \(1,2,\ldots,n\) 的两个排列 \(P_1\) 和 \(P_2\),求它们的最长公共子序列。

输入格式

第一行是一个数 \(n\)。

接下来两行,每行为 \(n\) 个数,为自然数 \(1,2,\ldots,n\) 的一个排列。

输出格式

一个数,即最长公共子序列的长度。

输入输出样例 #1

输入 #1

5 
3 2 1 4 5
1 2 3 4 5

输出 #1

3

说明/提示

  • 对于 \(50\%\) 的数据, \(n \le 10^3\);
  • 对于 \(100\%\) 的数据, \(n \le 10^5\)。

原来的想法

不知道变通,一味想着已有的模型。原来dp[i][2] 表示a序列第i个数加不加在最长子序列里面,但还要保留前面每个状态的最后的数在b序列的位置,判断怎么加。前面初值也比较难赋。若循环找前面能加的最长公共子序列,O(n^2),\(10^5\)会TLE,也不知道逻辑细节对不对。
代码:

点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n,ans;
int a[100010],b[100010],pos[100010];    //映射b的位置
int dp[100010][2][2];    //该位置不参与与参与最长自序列长度和最长子序列在b序列最后的位置
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++)
    {
        dp[i][0][0] = max(dp[i-1][0][0],dp[i-1][1][0]);
        dp[i][0][1] = (dp[i-1][1][0] > dp[i-1][0][0] ? dp[i-1][1][1] : dp[i-1][0][1]);
        for(int j = i-1;j >= 0;j--)
        {
            if(pos[a[i]] > pos[dp[j][0][1]])
            {
                if(pos[a[i]] > pos[dp[j][1][1]] && dp[j][1][0] > dp[j][0][0])
                {
                    dp[i][1][0] = dp[j][1][0] + 1;
                }
                else    dp[i][1][0] = dp[j][0][0] + 1;
                dp[i][1][1] = a[i];
                break;
            }
            if(j == 0)
            {
                dp[i][1][0] = 1;
                dp[i][1][1] = a[i];
            }
        }
    }
    //cout<<dp[1][1][1]<<endl;
    for(int i = 1;i <= n;i++)    ans = max(max(ans,dp[i][1][0]),dp[i][0][0]);
    cout<<ans;
    return 0;
}

/*
注意:观察题目,俩数列都是1~n的全排列
思想:有点贪心思想,每个数字可以加在最长子序列后面,找到前面符合条件的最长;也可以不使用该数字,状态存前一个数字的最大值
*/

正解

豆包提出转化为最长上升子序列,自己想了一下。如果映射B组数列,会有个1~n的映射,对应到A组就是最长上升子序列,跟之前的想法差不多,只是更加具体化了,序列A每个数字在序列B映射成先后顺序,小的在前面才可能公共。直接用贪心+二分,n*logn 级别。还是要多一些模型迁移能力。
代码:

点击查看代码
int n,ans;
int a[100010],b[100010],pos[100010];    //映射b的位置
vector<int> v;
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;
    }
    v.push_back(pos[a[1]]);
    for(int i = 2;i <= n;i++)
    {
        if(pos[a[i]] > v.back())    v.push_back(pos[a[i]]);
        else
        {
            auto it = upper_bound(v.begin(),v.end(),pos[a[i]]);
            *it = pos[a[i]];
        }
    }
    cout<<v.size();
    return 0;
}
posted @ 2026-08-03 00:14  lucky_lh  阅读(6)  评论(0)    收藏  举报