最长公共子序列(动态规划法+优化)
最长公共子序列(LCS)(1)
题目描述
给出1-n的两个排列P1和P2,求它们的最长公共子序列。
输入
第一行是一个数n;(n是5~1000之间的整数)数据范围小,可以用二维数组dp
接下来两行,每行为n个数,为自然数1-n的一个排列(1-n的排列每行的数据都是1-n之间的数,但顺序可能不同,比如1-5的排列可以是:1 2 3 4 5,也可以是2 5 4 3 1)。
输出
一个整数,即最长公共子序列的长度。
样例输入
5
3 2 1 4 5
1 2 3 4 5
样例输出
3
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
int []a=new int[n+1];
int []b=new int[n+1];
int [][] dp=new int[n+1][n+1];
for(int i=1;i<=n;i++){
a[i]=scan.nextInt();
}
for(int i=1;i<=n;i++){
b[i]=scan.nextInt();
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(a[i]==b[j]){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
}
}
}
System.out.println(dp[n][n]);
}
}
最长公共子序列(LCS)(2)
题目描述
给出1-n的两个排列P1和P2,求它们的最长公共子序列。
和最长公共子序列(LCS)(1)问题不同的是,本题的n在5-100000之间。数据范围大,要用映射的做法把问题转化为求最长递增子序列的长度
输入
第一行是一个数n;(n是5-100000之间的整数)
接下来两行,每行为n个数,为自然数1-n的一个排列(1-n的排列每行的数据都是1-n之间的数,但顺序可能不同,比如1-5的排列可以是:1 2 3 4 5,也可以是2 5 4 3 1)。
输出
一个整数,即最长公共子序列的长度。
样例输入
5
3 2 1 4 5
1 2 3 4 5
样例输出
3
提示
对于50%的数据,n≤1000
对于100%的数据,n≤100000
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
int[] p1=new int[n];
int[] p2=new int[n];
for(int i=0;i<n;i++){
p1[i]=scan.nextInt();
}
for(int i=0;i<n;i++){
p2[i]=scan.nextInt();
}
//映射
int[] pos=new int[n+1];
for(int i=0;i<n;i++){
pos[p2[i]]=i;
}
int[] nums=new int[n+1]; //[2,1,0,3,4]
for(int i=0;i<n;i++){
nums[i]=pos[p1[i]];
}
//求nums的最长递增子序列
int[] tails=new int[n];
int len=0;
for(int i=0;i<n;i++){
int num=nums[i];
int left=0;
int right=len-1;
int insertpos=len;
while (left<=right){
int mid=left+(right-left)/2;
if(tails[mid]<num){
left=mid+1;
}else{
insertpos=mid;
right=mid-1;
}
}
tails[insertpos]=num;
if(insertpos==len){
len++;
}
}
System.out.println(len);
}
}