原题链接:【模板】最长公共子序列 - 洛谷
题目描述
给出 1,2,…,n 的两个排列P1 和 P2 ,求它们的最长公共子序列。
输入格式
第一行是一个数 n。
接下来两行,每行为 n 个数,为自然数 1,2,…,n 的一个排列。
输出格式
一个数,即最长公共子序列的长度。
输入
5 3 2 1 4 5 1 2 3 4 5
输出
3
说明/提示
对于 50%50% 的数据, n≤10^3;
对于 100%100% 的数据,n≤10^5。
解题思路:
最长公共子序列有两种解法,分别是朴素解法和一种二分优化的解法,此题10^5,若用第一种朴素解法肯定会TLE,所以下面我们详细介绍第二种解法。
朴素解法(会TLE)
很明显我们去枚举序列1的每一位和序列2的每一位,如果两个数字相等,那么dp[i][j]=dp[i-1[j-1]+1。最后计算dp[n][n]即可。
代码实现:
#include<iostream> using namespace std; const int N=1005; int dp[N][N],a1[N],a2[N],n; int main() { //dp[i][j]表示两个串从头开始,直到第一个串的第i位 //和第二个串的第j位最多有多少个公共子元素 cin>>n; for(int i=1;i<=n;i++)cin>>a1[i]; for(int i=1;i<=n;i++)cin>>a2[i]; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { dp[i][j]=max(dp[i-1][j],dp[i][j-1]); if(a1[i]==a2[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1); //因为更新,所以++; } cout<<dp[n][n]<<endl;; return 0; }
优化解法
主要跟最长上升子序列的优化方法一样的,记住这句话就可以,“大则添加,小则替换”,这就是实现的思路,当此时要进入的值大于最长子序列的最后值就添加,若小于最长子序列的最后的值,则找到最长子序列中第一个大于此值的下标把它给替换掉。
代码实现:
#include<iostream>
using namespace std;
const int N=1e5+5;
int n,len=1;
int a[N],b[N],dp[N],map[N];//mapA映射B,相当于A数组当标准,操作B数组,压缩为一个数组,
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],map[a[i]]=i;//map映射
for(int i=1;i<=n;i++)cin>>b[i],dp[i]=0x3f3f3f;//初始无穷大
for(int i=1;i<=n;i++){
if(map[b[i]]>dp[len])dp[++len]=map[b[i]];//大则添加
else dp[lower_bound(dp,dp+len,map[b[i]])-dp]=map[b[i]];//小的替换,lower_bound实现更简单
}
cout<<len<<endl;//最后输出长度即可
return 0;
}
题后总结:
对于一般解法不需要练习,掌握好最优解法即可,可以多去洛谷练习此类题目,LCS经典问题是算法选手的入门算法,务必学会,文章尚有不足,恳请各位大佬指出。