子序列

【模板】最长公共子序列

题目描述

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

输入格式

第一行是一个数 \(n\)

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

输出格式

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

样例 #1

样例输入 #1

5 
3 2 1 4 5
1 2 3 4 5

样例输出 #1

3

提示

  • 对于 \(50\%\) 的数据, \(n \le 10^3\)
  • 对于 \(100\%\) 的数据, \(n \le 10^5\)

AC

点击查看代码
const int maxn = 1e5 + 10;
int a[maxn], b[maxn];
void solve() {
    int n;
    cin >> n;
    map<int, int> ma;//ai在a的位置
    for (int i = 2; i <= n; i++) {
        cin >> a[i];
        ma[a[i]] = i;
    }
    for (int i = 2; i <= n; i++) {
        cin >> b[i];
    }
    vector<int> dp(maxn, 0);
    // dp记长度i为公共序列的最后一个数在a的位置
    dp[1]   = ma[b[1]];
    int len = 1;
    for (int i = 2; i <= n; i++) {
        int id = ma[b[i]];
        // id在序列之后/直接加入
        if (id > dp[len]) { dp[++len] = id; }
        // 之前/利用二分更新最小的位置
        else {
            int l = 1, r = len, res = 1;
            while (l <= r) {
                int mid = (l + r) >> 1;
                if (dp[mid] > id) {
                    r   = mid - 1;
                    res = mid;
                }
                else { l = mid + 1; }
            }
            dp[res] = id;
        }
    }
    cout << len;
}

对于最长上升子序列(LIS)类问题

点击查看代码
for(int i=1;i<=n;i++)
	{
		dp[i]=1;//初始化
		for(int j=1;j<i;j++)//枚举i之前的每一个j
		if(data[j]<data[i] && dp[i]<dp[j]+1)
		//用if判断是否可以拼凑成上升子序列,
		//并且判断当前状态是否优于之前枚举
		//过的所有状态,如果是,则
		dp[i]=dp[j]+1;//更新最优状态
	}

nlogn

点击查看代码
for(int i=2;i<=n;i++)
	{
		int l=0,r=len,mid;
		if(a[i]>f[len])f[++len]=a[i];
		//如果刚好大于末尾,暂时向后顺次填充 
		else 
		{
		while(l<r)
		{	
		    mid=(l+r)/2;
		    if(f[mid]>a[i])r=mid;
	//如果仍然小于之前所记录的最小末尾,那么不断
	//向前寻找(因为是最长上升子序列,所以f数组必
	//然满足单调) 
			else l=mid+1; 
		}
		f[l]=min(a[i],f[l]);//更新最小末尾 
     	}
    }

对于最长公共子序列(LCS)问题,如本题

image

nlogn

利用map对a数组位置标记,看做b数组在寻找公共子序列的权(大小)
//大的尽量放在小的后面
将其等价做LIS问题

posted @ 2024-06-26 14:04  uanQ  阅读(13)  评论(0编辑  收藏  举报