最长公共子序列

23 篇文章 0 订阅
8 篇文章 0 订阅
这篇博客介绍了如何将最长公共子序列问题转换为最长上升子序列问题,以优化算法的时间复杂度。通过建立映射关系,将两个整数序列转化为下标序列,并使用二分查找优化查找过程,最终达到nlogn的时间复杂度。文中提供了C++、Java和Go三种语言的代码实现,但Go代码存在超时问题。
摘要由CSDN通过智能技术生成

题目描述
给出两个长度为 n 的整数序列,求它们的最长公共子序列(LCS)的长度,保证第一个序列中所有元素都不重复。

注意:

第一个序列中的所有元素均不重复。
第二个序列中可能有重复元素。
一个序列中的某些元素可能不在另一个序列中出现。
输入格式
第一行包含一个整数 n。

接下来两行,每行包含 n 个整数,表示一个整数序列。

输出格式
输出一个整数,表示最长公共子序列的长度。

数据范围
1≤n≤106,
序列内元素取值范围 [1,106]。

样例
输入样例1:
5
1 2 3 4 5
1 2 3 4 5
输出样例1:
5
输入样例2:
5
1 2 3 5 4
1 2 3 4 5
输出样例2:
4

算法1
(暴力枚举) nlogn
求公共子序列问题如何转换为求最长上升子序列问题?
答: 因为公共子序列中值的相同的,所以数组2中公共子序列中的值放到数组1中数组下标仍然相同
所以:求公共子序列因为不用求具体的序列是多少,我们转化为求公共子序列的下标,因为下标是递增的!!!
所以转化为求最长上升子序列,这个序列是值下表的序列
因为子序列的定义是,组成子序列的元素在原数组的下标一定是严格单调递增的。

所以我们可以将两个数组转换成一个下标数组,借助辅助数组来保存第一个数组各元素的下标。
再通过第二个数组中的元素对应的下标,组成我们的下标数组。

之后就可以用基础课里讲的nlogn的最长上升子序列解法啦

时间复杂度 nlogn
参考文献
C++ 代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6+10;
int n,len;
int a[N];
int st[N];
int main()
{
    cin>>n;
    memset(a, -1, sizeof a);
    for (int i = 1; i <= n; i ++ )
    {
        int x;
        scanf("%d",&x);
        a[x]=i;
    }
    for (int i = 1; i <= n; i ++ )
    {
        int x;
        scanf("%d",&x);
        int k=a[x];
        if(k==-1)continue;
        int l=0,r=len;
        //二分找到最大的小于当前数组下标k的值
        while(l<r)
        {
            int mid=(l+r+1)>>1;//另外一个模板也是可以的(l+r)>>1
            if(st[mid]<k)l=mid;
            else r=mid-1;
        }
        st[r+1]=k;
        len=max(len,r+1);
    }
    return cout<<len,0;
}

Java代码 Scanner 会超时,有时候还得看BufferedReader这位“大佬”

import java.util.*;
import java.io.*;
public class Main{
    public static void main(String[] args)throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        String [] u = br.readLine().split(" ");
        String [] v = br.readLine().split(" ");
        int[] a = new int[n];
        int[] b = new int[n];
        for(int i=0;i<n;i++)a[i]=Integer.parseInt(u[i]);
        for(int i=0;i<n;i++)b[i]=Integer.parseInt(v[i]);
        int[] pos = new int[1000010];
        Arrays.fill(pos, -1);
        for(int i=0;i<n;i++)pos[a[i]]=i;
        List<Integer>idx=new ArrayList<>();
        for(int i=0;i<n;i++)
            if(pos[b[i]]!=-1)idx.add(pos[b[i]]);
        int[] q = new int[idx.size() + 1];
        int len = 0;
        for(int i=0;i<idx.size();i++)
        {
            int l=0,r=len;
            while(l<r)
            {
                int mid=(l+r+1)>>1;
                if(q[mid]<idx.get(i))l=mid;
                else r=mid-1;
            }
            q[r+1]=idx.get(i);
            len=Math.max(len,r+1);
        }
        System.out.println(len);
    }
}

GO代码 TLE 超时 我不知道怎么改进,请大佬请教(不过应该也没人看这个)😭😭😭

package main
import  "fmt"
const N=1000110
var(
     id[N]int
     q[N]int 
)
func max(a,b int)int{
    if a > b{
        return a
    }
    return b
}
func main(){
    var n,len,x int
    fmt.Scanf("%d",&n)
    for i:= 0; i < 1000110; i ++ {
        id[i] = -1
    } 
    for i:= 0; i < n; i ++ {
        fmt.Scanf("%d",&x)
        id[x] = i
    }
    len = 0
    q[0] = -1
    for i:= 0; i < n; i ++{
        var x,k,l,r int 
        fmt.Scanf("%d",&x)
        k = id[x]
        if k==-1{
            continue
        }
        l=0
        r=len
        for {
            if l<r{
                var mid int
                mid=(l+r+1)>>1
                if q[mid]<k{
                    l=mid
                }else {
                    r=mid-1
                }
            }else {
                break;
            }
        }
        q[r+1]=k
        len=max(len,r+1)
    } 
    fmt.Println(len)
}

欢迎留言点赞

嗷嗷嗷~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值