Daimayuan Online Judge 最长公共子序列

代码源每日一题

给出从 11 到 nn 的两个排列 P1P1 和 P2P2,求它们的最长公共子序列。

输入格式

第一行是一个正整数 nn。

接下来两行,每行为 nn 个数,为自然数 1,2,…,n1,2,…,n 的一个排列。

输出格式

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

数据范围

1≤n≤1051≤n≤105

输入样例

5 
3 2 1 4 5
2 1 3 4 5

输出样例

4

思路:这个题我们只需要找出相同的序列即可,也就是我们不需要找出数字之间的大小,与这个题比较相似的有:最长上升子序列、最长公共子序列(范围较小的dp写法)。我们想要找出公共的子序列是不是只要数字的出现顺序即可。就像假如两个数组:1都在3的后面,4都在1的后面,5都在4的后面,那么最长的子序列就是3 1 4 5.只与顺序有关。将第一个数组a 的顺序记录为1 2 3 4 5,然后将后一个数组b的值记录为在a中出现的顺序,(为什么不需要记录下来原值?因为与原值的大小无关,只与顺序有关)。举例如:a 值为3 2 1 4 5,顺序为1 2 3 4 5;b值为1 2 3 4 5,顺序为3 2 1 4 5.这里需要明白顺序小的出现在后面说明与原序列不符,因为序列是有顺序的,所以我们来考虑顺序问题(考虑顺序的问题肯定是与顺序的大小值有关了):那么开始观察顺序的大小,是不是只要是上升的顺序就不会违背,影响第一个数组的顺序,所以我们要在b数组中找到最大上升子序列即可。

1e5数据范围的最长上升子序列(二分):小数据范围可以用dp来处理,但是大到1e5时就需要nlogn的方法来解决了。二分长度区间我们可以将n个点的数依次找到合适他的长度范围,让以他为结尾。在长度扩大时,(最大)长度的最后一个的最小值一定增大,是递增的。当这个数较小时是不是可以将它插入到之前存在的长度大小之中,如果之前的数没有大于当前数的,是不是要新扩展长度,是不是长度会变大1,是不是我们只要用二分找到数组中长度为mid,且最后结尾的最大值小于b[i]即可,也就是最小中找最大,我们当前数比最大长度的数组值还大,所以要往后推一位长度,记录在下一个长度中。然后更新结尾值,记录最大长度即可。

最长上升子序列模板:

cin>>n;
for(int i=1; i<=n; i++)
{
    cin>>a[i];
}

tail[0]=-2e9;
int len=0;
for(int i=1; i<=n; i++)
{
    int l=0,r=len;
    while(l<r)
    {
        int mid=l+r+1>>1;
        if(tail[mid]<a[i])
            l=mid;
        else
            r=mid-1;
    }
    len=max(len,r+1);
    tail[r+1]=a[i];
}
cout<<len<<endl;

完整代码:

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> pii;
typedef long long ll;
typedef vector<int> vi;
//#define int long long
#define fir first
#define sec second
#define all(x) (x).begin(), (x).end()
#define sz(x) (int)x.size()
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define repd(i, l, r) for (int i = l; i >= r; --i)
#define pb push_back

const int N=1e5+10;
int a[N],b[N];
int tail[N];
unordered_map<int,int>mp;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int n;
    int x;
    cin>>n;
    rep(i,1,n) cin>>a[i],mp[a[i]]=i;
    rep(i,1,n) cin>>x,b[i]=mp[x];

    int len=0;
    tail[0]=-2e9;
    rep(i,1,n)
    {
        int l=0,r=len;
        while(l<r)
        {
            int mid=l+r+1>>1;
            if(tail[mid]<b[i])l=mid;
            else r=mid-1;
        }
        len=max(len,r+1);
        tail[r+1]=b[i];
    }
    cout<<len<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值