上海交大2019考研机试第一题:以时间复杂度为nlogn的算法解决最长公共子序列(LCS)问题

给出两个数字序列,求最长公共子序列(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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章