洛谷题单指南-动态规划2-P1439 【模板】最长公共子序列

原题链接:https://www.luogu.com.cn/problem/P1439

题意解读:求最长公共子序列的长度。

解题思路:

1、O(n^2)的方法:动态规划

设两个排列为a,b

设dp[i][j]表示a[1~i]与b[1~j]的最长公共子序列长度

根据公共子序列结尾是否不包含a[i]、b[j]分情况讨论:

结尾不包含a[i]或者不包含b[j],a[i]和b[j]相等不相等都可以

  不包含a[i]:所以dp[i][j] = dp[i-1][j],即a[1~i]与b[1~j]的最长公共子序列等同于a[1~i-1]与b[1~j]的最长公共子序列

  不包含b[j]:所以dp[i][j] = dp[i][j-1],即a[1~i]与b[1~j]的最长公共子序列等同于a[1~i]与b[1~j-1]的最长公共子序列

结尾包含a[i]和b[j],必须满足a[i] == b[j]

  结尾包含a[i]和b[j],所以dp[i][j] = dp[i-1][j-1] + 1,即a[1~i]与b[1~j]的最长公共子序列等同于a[1~i-1]与b[1~j-1]的最长公共子序列再加上1

对以上情况求max即可。

由于空间和时间复杂度都是O(n^2),因此只能处理n=1000的情况。

50分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1005; //注意不能写100005,提交后数组内存超出限制,编译失败,但本地看不出来
int a[N], b[N];
int dp[N][N];
int n;

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) cin >> b[i];

    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= n; j++)
        {
            dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
            if(a[i] == b[j]) dp[i][j] = max(dp[i][j], dp[i-1][j-1] + 1);
        }
    }
    cout << dp[n][n];

    return 0;
}

2、O(logn)的方法:LIS+二分

由于两个排列a、b都是1~n组成,只是顺序不同

举个例子:a = {3, 2, 1, 4, 5},b = {1, 2, 3, 4, 5}

用一个桶数组h[]保存a中每个元素的位置:

h[3] = 1, h[2] = 2, h[1] = 3, h[4] = 4, h[5] = 5

再定义一个数组c用来表示b数组每个元素在a数组中的位置:

c = {3, 2, 1, 4, 5}

由于a中每个元素的下标是递增的,所以如果b中元素对应的a的位置是递增(也就是c中元素递增),那么可以认为和a是公共子序列

因此问题转换为:计算c中最长上升子序列的长度

要实现O(logn)的算法,可以采用此文介绍的单调栈+二分的方式进行优化。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 100005;
int a[N], b[N], h[N], c[N];
int s[N], top;
int n;

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        h[a[i]] = i;
    } 
    for(int i = 1; i <= n; i++)
    {
        cin >> b[i];
        c[i] = h[b[i]];
    } 

    for(int i = 1; i <= n; i++)
    {
        if(top == 0 || s[top] < c[i]) s[++top] = c[i];
        else
        {
            int l = 1, r = top, ans = -1;
            while(l <= r)
            {
                int mid = (l + r) >> 1;
                if(s[mid] >= c[i])
                {
                    ans = mid;
                    r = mid - 1;
                }
                else l = mid + 1;
            }
            s[ans] = c[i];
        }
    }
    cout << top;

    return 0;
}

 

posted @ 2024-04-26 11:02  五月江城  阅读(63)  评论(0编辑  收藏  举报