给出两个数字序列,求最长公共子序列(LCS)。 保证一个序列中所有元素都不重复,第一行给定一个n 为序列的长度,第二第三行为两个序列。
其中 60% 的用例 n<=1000,所有用例保证 n<= 1000000
Sample Input
5
1 2 3 4 5
1 2 3 4 5
Sample Output
5
Sample Input
5
1 2 3 5 4
1 2 3 4 5
Sample Output
4
提炼题目:此题要求以时间复杂度为nlogn的算法解决最长公共子序列问题
- 条件:所给两个序列中均没有重复元素
- 要求:数据规模为1e6,要求在10s内完成,亦即常规n^2复杂度不能解决此题
- 解法:首先,由于序列中没有重复元素,可以把一个字符串按顺序映射为 1,2,3,4,……并用数组记录下来,这样字符串1就变成了一个连续递增的字符串;然后,再讲字符串为按照字符串1得到的映射字符数组映射为新的字符串3;如此,问题即转化为在字符串3中寻找1,2,3,……这样连续递增的字符子串的最大长度;显然,在字符串3中寻找连续递增字符串的最大长度,则最长公共子序列问题(LCS)即被巧妙转化为最长递增子序列问题(LIS)。LIS的动态规划算法的改进算法的时间复杂度即为nlogn。
- 代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <climits>
using namespace std;
const int INF = INT_MIN;
const int MAXN = 1e6+1;
int seq[MAXN];
int mapTable[MAXN];
int minRearElem[MAXN];
int len;
int main() {
int n;
while (scanf("%d", &n) != EOF) {
memset(mapTable, 0, sizeof(mapTable));
int x;
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
mapTable[x] = i;
}
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
seq[i] = mapTable[x];
}
len = 0;
minRearElem[len] = INF;
for (int i = 1; i <= n; i++) {
if (seq[i] >= minRearElem[len]) {
len++;
minRearElem[len] = seq[i];
}
else {
int low = 1, high = len;
while (low < high) {
int mid = (low + high) / 2;
if (minRearElem[mid] > seq[i]) {
high = mid-1;
}
else if (minRearElem[mid] < seq[i]) {
low = mid + 1;
}
else {
break;
}
}
minRearElem[low] = seq[i];
}
}
printf("%d\n", len);
}
return 0;
}