Common Subsequence & luoguP1439(DP之LCS最长公共子序列问题+利用O(nlogn)的LIS优化)

该博客探讨了一种优化动态规划算法的方法,用于解决寻找两个序列的最大公共子序列问题。在面对数据规模达到10^5时,原始的O(n^2)解决方案可能导致超时,因此引入了最长递增子序列(LIS)的优化。通过将一个序列映射到另一个序列,并利用队列和二分查找,将复杂度降低到O(nlogn)。博客提供了详细的代码实现,展示了如何在没有重复元素的情况下,利用LIS优化解决大规模问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, …, xm > another sequence Z = < z1, z2, …, zk > is a subsequence of X if there exists a strictly increasing sequence < i1, i2, …, ik > of indices of X such that for all j = 1,2,…,k, xij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.

Input
The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.

Output
For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.

Sample Input
abcfbc abfcab
programming contest
abcd mnp

Sample Output
4
2
0

状态转移方程:
在这里插入图片描述

代码:

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int dp[10005][10005];
int main()
{
	string a,b;
	while(cin>>a>>b)
	{
		for(int i=1;i<=a.size();++i)
		{
			for(int j=1;j<=b.size();++j)
			{
				if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1;
				else dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
			}
		}
		cout<<dp[a.size()][b.size()]<<endl;	
	}
	return 0;
}

下面是利用LIS优化:
在这里插入图片描述
很明显这题的数据范围在1e5,用上面的O(n^2)的算法会TLE
所以要利用LIS优化,而且LIS也要用队列+二分进行优化,这样复杂度就会降到O(nlogn),但是使用这一优化方法的前提是两个数组中的元素均无重复且相同,具体优化的思路是啥呢,默认a数组的元素序列是有序的上升序列,然后将a数组中元素的映射也映射到b数组中,这样就得到了b数组中的每个元素在a数组中排第几位,然后对b数组做LIS即可。
下面是代码:

#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
map<int,int> mp;
inline int read()
{
    int s = 0, w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}
int qu[100005];//队列 
int a[100005],b[100005];
int qu_i=0,temp;
int main()
{
	int n;
	ll ans=0;
	n=read();
	for(int i=1;i<=n;++i)
	{
		a[i]=read();
		mp[a[i]]=i;//做一个映射 
	}
	for(int i=1;i<=n;++i)
	{
		b[i]=read();
		b[i]=mp[b[i]];//映射到b,得到b的每个元素在a中是第几位 
	}
	qu[qu_i++]=b[1];
	for(int i=2;i<=n;++i)
	{
		if(b[i]>qu[qu_i-1]) qu[qu_i++]=b[i];//如果b[i]大于数列中最大的数字,则直接入队 
		else 
		{
			temp=lower_bound(qu,qu+qu_i,b[i])-qu;//寻找队列中第一个大于等于b[i]的元素,注意返回值是地址,要得到数组下标则要减去数组首地址 
			qu[temp]=b[i];//替换那个元素,实际上是有贪心的成分在里面,因为替换了这个元素后,LIS的长度不变,但是在之后会有另一个元素加进来的几率增大了 
		}
	}
	cout<<qu_i<<endl;//输出队列长度即为答案 
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值