最长公共子序列问题【LCS】

算法相关的学习笔记与代码记录。

最长公共子序列问题【LCS】 的统一封面

题目简义

对于给定的序列AA和BB
要求求出最长序列MM使得M⊆AM \subseteq A且M⊆BM \subseteq B

通解(无特殊性质)

来源
该算法时间复杂度为O(n2)O(n^{2})
我们用f(i,j)f(i, j)表示AA序列只考虑前ii个元素BB序列只考虑前jj个元素的最长长度
那么我们进行分类讨论f(i,j)f(i, j)的情况

  1. AiA_{i}与BjB_{j}若不匹配,则有f(i,j)=max(f(i,j−1),f(i−1,j))f(i, j) = max(f(i,j - 1),f(i - 1, j))
  2. AiA_{i}与BjB_{j}相匹配,则有f(i,j)=f(i−1,j−1)+1f(i, j) = f(i - 1, j -1 ) + 1

以此枚举ii和jj即可

for(int i = 1; i <= n; i ++){
	for(int j = 1; j <= n; j ++){
		f[i][j] = max(f[i - 1][j], f[i][j - 1]);
		if(a[i] == b[i])f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
	}
}

事实上,LCS问题还有一种变式

来源
对于原问题我们增加一个条件:A序列不存在重复元素
通常要求在1e5的范围内求解,若用第一种方法,很显然会TLE或MLE
我们更换思路,引入序列Bi′B'_{i}表示BiB_{i}在AiA_{i}中的问题
则对于原序列MM在B′B'中对应的数呈上升关系,即可将LIS问题转化为LCS问题
然后使用LCS的二分优化O(nlogn)O(nlogn)时间复杂度下求解

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 1e6 + 10;

int q[N], id[N];

int main(){
	memset(id, -1, sizeof id);
	
	int n;
	scanf("%d", &n);
	
	for(int i = 1; i <= n; i ++){
		int x;
		scanf("%d", &x);
		id[x] = i;
	}
	
	int tt = 0;
	q[0] = -1;
	for(int i = 1; i <= n; i ++){
		int x;
		scanf("%d", &x);
		
		if(id[x] == -1)continue;
		int k = id[x];
		
		int l = 0, r = tt;
		while (l < r){
			int mid = l + r + 1 >> 1;
			if(k > q[mid])l = mid;
			else r = mid - 1;
		}
		
		q[r + 1] = k;
		tt = max(r + 1, tt);
	}
	
	printf("%d", tt);
	
	return 0;
	
}

互动

0 人点赞 · 0 条评论


© 2026 By Catkin

Powered by Astro