給出兩個數字序列,求最長公共子序列(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;
}