题目描述
给出两个长度为 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)
}
欢迎留言点赞
嗷嗷嗷~