最长上升(非递减)子序列(LIS)和最长公共子序列(LCS)
896. 最长上升子序列 II
给定一个长度为 \(N\) 的数列,求数值严格单调递增的子序列的长度最长是多少。
输入格式
第一行包含整数 \(N\)。
第二行包含 \(N\) 个整数,表示完整序列。
输出格式
输出一个整数,表示最大长度。
数据范围
\(1≤N≤100000\),
\(−10^9≤数列中的数≤10^9\)
输入样例:
7
3 1 2 1 8 5 6
输出样例:
4
提示
\(f[i]\) 表示长度为 \(i\) 且序列结尾最小的值
- 时间复杂度:\(O(nlogn)\)
代码
#include<bits/stdc++.h>
using namespace std;
int n,f[100005];
int main()
{
scanf("%d",&n);
int x,len=0;
f[0]=-0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
f[i]=0x3f3f3f3f;
scanf("%d",&x);
if(x>f[len])f[++len]=x;
else
{
int pos=lower_bound(f+1,f+1+len,x)-f;
f[pos]=x;
}
}
printf("%d",len);
return 0;
}
- 最长非递减子序列
int LNDS(vector<int> nums)
{
//f[i]表示长度为i,最长非递减子序列结尾的最小值
vector<int> f;
f.push_back(nums[0]);
int n=nums.size();
for(int i=1;i<n;i++)
{
if(nums[i]>=f.back())f.push_back(nums[i]);
else
{
int pos=upper_bound(f.begin(),f.end(),nums[i])-f.begin();
f[pos]=nums[i];
}
}
return f.size();
}
3510. 最长公共子序列
给出两个长度为 \(n\) 的整数序列,求它们的最长公共子序列(LCS)的长度,保证第一个序列中所有元素都不重复。
注意:
- 第一个序列中的所有元素均不重复。
- 第二个序列中可能有重复元素。
- 一个序列中的某些元素可能不在另一个序列中出现。
输入格式
第一行包含一个整数 \(n\)。
接下来两行,每行包含 \(n\) 个整数,表示一个整数序列。
输出格式
输出一个整数,表示最长公共子序列的长度。
数据范围
\(1≤n≤10^6\),
序列内元素取值范围 \([1,10^6]\)。
输入样例1:
5
1 2 3 4 5
1 2 3 4 5
输出样例1:
5
输入样例2:
5
1 2 3 5 4
1 2 3 4 5
输出样例2:
4
提示
将第一个序列离散化,即将值与其下标对应起来,再将第二个序列中的值对应起来,没有在第一个序列出现过的值不用管,下标显然是上升的,即转化为求第二个对于序列的 \(LIS\)~
- 时间复杂度:\(O(nlogn)\)
代码
#include<bits/stdc++.h>
using namespace std;
int n,x,f[1000005],id[1000005];
int main()
{
scanf("%d",&n);
memset(id,-1,sizeof id);
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
id[x]=i;
f[i]=0x3f3f3f3f;
}
f[0]=0;
int len=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
if(id[x]==-1)continue;
if(id[x]>f[len])f[++len]=id[x];
else
{
int pos=lower_bound(f+1,f+1+len,id[x])-f;
f[pos]=id[x];
}
}
printf("%d",len);
return 0;
}