p1439最长公共子序列

## 题目描述

给出 $1,2,\ldots,n$ 的两个排列 $P_1$ 和 $P_2$ ,求它们的最长公共子序列。

## 输入格式

第一行是一个数 $n$。

接下来两行,每行为 $n$ 个数,为自然数 $1,2,\ldots,n$ 的一个排列。

## 输出格式

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

## 样例 #1

### 样例输入 #1

```

3 2 1 4 5
1 2 3 4 5
```

### 样例输出 #1

```
3
```

## 提示

- 对于 $50\%$ 的数据, $n \le 10^3$;
- 对于 $100\%$ 的数据, $n \le 10^5$。

做法一:score:50(dp加暴力做法),但是会超时

dp[i][j]表示取第一个串的从1到第i位元素和取第二个串的从1到j位元素的最长公共子序列

用二重循环遍历i,j

如果元素相等a1[i]==a2[j]  就更新长度  dp[i][j]=dp[i-1][j-1]+1;

如果不相等,就继承最长的状态  dp[i][j]=max(dp[i-1][j],dp[i][j-1]);

最后输出dp[n][n]即为答案

代码如下:(这里的m就是n,当时没改)

#include<iostream>
using namespace std;
int dp[1001][1001],a1[2001],a2[2001],n,m;
int main()
{
   //dp[i][j]表示两个串从头开始,直到第一个串的第i位 
   //和第二个串的第j位最多有多少个公共子元素 
   cin>>n;
   m=n;
   for(int i=1;i<=n;i++)scanf("%d",&a1[i]);
   for(int i=1;i<=m;i++)scanf("%d",&a2[i]);
   for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
     {
     	dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
     	if(a1[i]==a2[j])
     	dp[i][j]=dp[i-1][j-1]+1;
     	//因为更新,所以++; 
     }
   cout<<dp[n][m];
}

做法二:满分做法(用映射和最长上升子序列的思想)

下面的说明来自洛谷的一个高手的解释,在此引用一下

A:3 2 1 4 5

B:1 2 3 4 5

我们不妨给它们重新标个号:把3标成a,把2标成b,把1标成c……于是变成:

A: a b c d e
B: c b a d e

这样标号之后,LCS长度显然不会改变。但是出现了一个性质:

两个序列的子序列,一定是A的子序列。而A本身就是单调递增的。
因此这个子序列是单调递增的。

换句话说,只要这个子序列在B中单调递增,它就是A的子序列。

哪个最长呢?当然是B的LIS最长。

自此完成转化  //来自题解 P1439 【【模板】最长公共子序列】 - 洛谷专栏 (luogu.com.cn)

即先把第一个串的元素按顺序映射为1,2,3,4...

然后把第二串的数组元素根据上面的映射,改变他们相应的值,最后即求改变后的b数组的最长上升子序列长度即可,代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N],n,Index=1;
unordered_map <int,int> m;
bool check(int mid,int x){
	if(b[mid]<=x)
	return true;
	return false;
}
int leng(int b[],int ll){
	int cnt=1;
	b[1]=b[1]; 
	for(int i=2;i<=ll;i++){
		if(b[i]>b[cnt]){
			cnt++;
			b[cnt]=b[i];
		}
		else if(b[i]<b[cnt]){
			int l=1,r=cnt;
			while(l<r){
				int mid=l+r>>1;
				if(check(mid,b[i]))
				l=mid+1;
				else r=mid;
			}
//			printf("i=%d l=%d cnt=%d\n",i,l,cnt);
			b[l]=b[i];
		} 
	}
	return cnt;
}
//上面为求最长上升子序列长度的函数
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        m[a[i]]=Index;
        Index++;
    }
    for(int i=1;i<=n;i++)
        scanf("%d",&b[i]);
    for(int i=1;i<=n;i++)
        b[i]=m[b[i]];//求b数组的最长上升子序列长度
    printf("%d",leng(b,n));
	return 0;
}

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值