题意:有2个长度分别为p+1和q+1的序列,每个序列各个元素互不相同,都是1-n^2间的整数,
两个序列第一个元素为1,求两个序列的最大公共子序列长度?
2<=n<=250,1<=q,p<=n^2
题解: 本质上就是求2个数组AB的最长公共子序列,也就是LIS问题,但是朴素的LIS算法时间复杂度是O(n^2),对此题来说超时。根据蓝书上的讲解,正确方法是将A序列 的元素编号,比如A={1,7,5,4,8,3,9}则A数组改变为{1,2,3,4,5,6,7},
B={1,4,3,5,6,2,8,9},B数组变为={1,4,6,3,0,0,5,7},0表示没有在A中出现过,因为0不影响结果,所以把0去除后B={1,4,6,3,5,7},
相当于就是求该数组的最长子序列长度,最长子序列LIS算法的优化版本时间复杂度为O(nlogn),可以解决此题。
(LIS优化算法具体原理参考https://www.cnblogs.com/Bravewtz/p/10624796.html)
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 90000
using namespace std;
int n,p,q;
int a[MAXN],b[MAXN];
int number[MAXN];
int temp[MAXN];
int main() {
int Case = 1;
int T;
scanf("%d",&T);
while(T--) {
memset(number,0,sizeof(number));
memset(temp,0,sizeof(temp));
scanf("%d%d%d",&n,&p,&q);
for(int i = 0;i < p+1;i++) {
scanf("%d",&a[i]);
number[a[i]] = i+1;
}
int j = 0;
for(j = 0;j < q+1;j++) {
scanf("%d",&b[j]);
b[j] = number[b[j]];
}
int cnt = j;
memcpy(a,b,sizeof(b));
int l = 0;
int r = 0;
// erase 0 in array b
while(l < cnt) {
if(a[l])
b[r++] = a[l];
l++;
}
int res = 0;
for(int i = 0;i < r;i++) {
if(res == 0 || b[i] > temp[res-1]) {
temp[res++] = b[i];
} else {
int replace = lower_bound(temp,temp+res,b[i]) - temp;
temp[replace] = b[i];
}
}
printf("Case %d: %d\n",Case++,res);
}
return 0;
}