栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

GDUT - 专题学习2 B - 最长公共子序列

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

GDUT - 专题学习2 B - 最长公共子序列

B - 最长公共子序列 题目

给出 1∼n 的两个排列 P1​ 和 P2​,求它们的最长公共子序列。

输入格式

第一行是一个数 n (1≤n≤105)。

接下来两行,每行为 n 个数,为自然数 1∼n 的一个排列。

输出格式

一个数,即最长公共子序列的长度。

Sample Input

5 
3 2 1 4 5
1 2 3 4 5

Sample Output

3

思路 1.dp做法 O(n²)

首先要知道,对于单独的一个元素来说,其最长上升子序列就是它本身,所以dp的每一个元素一开始都应该是1

然后dp[i]可用于记录在 i 之前的每一个元素的最优解,如果当前的dp[i]大于之前的最优解,即可继承下去

for(int i = 1; i <= n; ++i)
{
    dp[i] = 1;//对dp数组进行初始化
    for(int j = 1; j < i; ++j)
    {
        if(a[j] < a[i] && dp[i] < dp[j] + 1)//先判断该是否为上升子序列,再判断子序列是否更长
        {
            dp[i] = dp[j] + 1;//如果是,则继承结果并加一
        }
    }
}

在本题的条件中,10^5显然会超时,所以此方法在本题不适用。

2.二分查找做法 O(nlogn)

仔细观察题目发现,两个序列都是从1到n的全排列,所以两个序列的元素都是相同的,只是位置不一样而已,这时候我们再用一个数组t将序列P1中的元素在序列P2中的位置表示出来。为什么呢?

假设有以上两个序列,为了更加直观我用小写字母按从小到大的顺序来表示P1中各元素 

这个时候 a—1 b—3 c—4 d—2 e—5

再用这些字母去替代P2

 

 这时候P2序列就变成了edbac,不难发现P2中的最长升序子序列为 bc 或 ac ,更巧的是其代表的元素 34 和 14 正好也是P1中的子序列,当然这不是个巧合,同学们深思一下就能想明白其中的道理了

这时候题目就变得简单了,我们只需再用一个数组存P1对应元素的顺序

for (int i = 1; i <= n; ++i)
{
	cin >> a[i];
	t[a[i]] = i;//将各元素所在位置存起来
}

再用这个顺序代替P2,最后在变化后的P2中找出其最长上升子序列就可以了,用二分随便搞搞就做完了。其中找最长上升子序列的方法在这里有细讲。

代码
#include
#include
using namespace std;
int n, p1[100005], p2[100005], t[100005], f[100005], len = 0;
//数组t用于存放p1中元素的位置,t[2] = 1就表示元素2在p1中出现的位置时第1位
//数组f则是用来存放替换后的数组p2
int main()
{
	cin >> n;
	for (int i = 1; i <= n; ++i)
	{
		cin >> p1[i];
		t[p1[i]] = i;
	}
	for (int i = 1; i <= n; ++i)
	{
		cin >> p2[i];
	}
	for (int i = 1; i <= n; ++i)
	{
		if (t[p2[i]] > f[len])//如果该元素大于上一个,说明可以接上去,然后再令最大值等于该元素
		{
			f[++len] = t[p2[i]];
		}
		else//如果不是,则找数组f中第一个大于等于该元素的元素位置,并将其接在后面
		{
			f[lower_bound(f + 1, f + len + 1, t[p2[i]]) - f] = t[p2[i]];
		}
	}
	cout << len;//最后输出最长的len
	return 0;
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/714437.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号