题解:P1439 【模板】最长公共子序列
Manchester_City_FC · · 题解
题目大意
给出
算法介绍
朴素解法
我们定义
- 如果
{P_1}_{i-1}={P_2}_{j-1} ,则有:dp_{i,j}=dp_{i-1_j-1}+1 。 - 否则
dp_{i,j}=\max(dp_{i-1,j},dp_{i,j-1}) 。
这个 dp 的时间复杂度为
优化 dp
由于两个序列均为
假设有两个排列
接下来求最长递增子序列(LIS)即可。
使用常见的二分查找方法,可以在
- 如果该元素大于
t 数组的最后一个元素,则直接追加; - 否则,二分查找找到
t 中第一个不小于该元素的位置,并更新该位置的值。
整体时间复杂度为
AC 代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1;
int n,a[N],b[N],pos[N],s[N],t[N],ans;
//pos 记录数字在第二个排列中的位置(数字范围 1~n)
//t 数组:t[i] 表示长度为 i+1 的递增子序列末尾最小的值
//ans 为最长递增子序列的当前长度
int main(){
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++){
cin>>b[i];
pos[b[i]]=i;//建立映射
}
for(int i=0;i<n;i++) s[i]=pos[a[i]];
for(int i=0;i<n;i++){
if(!ans||s[i]>t[ans-1]) t[ans++] = s[i];//当前元素比所有尾部元素都大,直接追加
else{
//使用二分查找在 t 中寻找第一个大于等于 s[i] 的位置
int l=0,r=ans-1;
while(l<=r){
int mid=l+(r-l)/2;
if(t[mid]>=s[i]) r=mid-1;
else l=mid+1;
}
t[l]=s[i];//更新找到的位置
}
}
//输出最长公共子序列的长度,即为 LIS 的长度
cout<<ans;
}