最长公共子序列

本文讲解了如何将最长公共子序列问题从O(n^2)优化到O(n log n),通过转换思路,利用元素在序列中的下标构建升序辅助数组,解决了寻找最长上升子序列的问题。通过实例演示和C++代码实现,深入剖析了动态规划在解决此类问题中的关键步骤。
摘要由CSDN通过智能技术生成

最长公共子序列

最长公共子序列是一个典型的二维线性动态规划问题,给定两个长度相同的序列,求这两个序列中的最长公共子序列。

例如:序列 1 , 3 , 5 , 4 , 9 1,3,5,4,9 1,3,5,4,9 5 , 7 , 4 , 9 , 3 5,7,4,9,3 5,7,4,9,3的最长公共子序列为 5 , 4 , 9 5,4,9 5,4,9

动态规划

定义状态为 d p [ i ] [ j ] dp[i][j] dp[i][j]为子序列 a a a到下标 i i i为止,子序列 b b b到下标 j j j为止的最长公共子序列的长度。因此我们有如下的动态规划方程:

如果 a [ i ] = b [ j ] a[i] = b[j] a[i]=b[j]

d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j] = dp[i-1][j-1] + 1 dp[i][j]=dp[i1][j1]+1

即为,如果该位相等,那么本状态就等于 a b ab ab的上一位加 1 1 1

如果 a [ i ] ≠ b [ j ] a[i] \neq b[j] a[i]=b[j]

d p [ i ] [ j ] = max ⁡ ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) dp[i][j] = \max(dp[i-1][j],dp[i][j-1]) dp[i][j]=max(dp[i1][j],dp[i][j1])

即为,如果该位不相等,那么就状态继承,两个同位状态决策出最优的状态。

#include <bits/stdc++.h>

#define FR freopen("in.txt","r",stdin);

using namespace std;

int a[100];
int b[100];

int dp[100][100];

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

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

特例

P1439

观察这个题的数据范围,发现上述 O ( n 2 ) O(n^{2}) O(n2)的算法必然TLE,那么我们只能转换思路。

例如序列:

3 , 2 , 1 , 4 , 5 1 , 2 , 3 , 4 , 5 3,2,1,4,5 \\ 1,2,3,4,5 3,2,1,4,51,2,3,4,5

我们知道最长的公共子序列一定是 b b b的子序列,同时也是 a a a的子序列,那么问题就转换成在 b b b中找到 a a a的最长上升子序列即可。跟普通的最长上升子序列问题不同,排序的关键字是元素在 a a a序列中的下标位置即转换成:

3 0 , 2 1 , 1 2 , 4 3 , 5 4 1 2 , 2 1 , 3 0 , 4 3 , 5 4 3_{0},2_{1},1_{2},4_{3},5_{4} \\ 1_{2},2_{1},3_{0},4_{3},5_{4} 30,21,12,43,5412,21,30,43,54

因此就是求 2 , 1 , 0 , 3 , 4 2,1,0,3,4 2,1,0,3,4的最长上升子序列即可。

#include <bits/stdc++.h>

#define FR freopen("in.txt","r",stdin);

using namespace std;

int idx[100005];
int g[100005];
int b[100005];

int main()
{
    FR
    int n;
    cin >> n;
    for(int i = 0; i<n; i++)
    {
        int val;
        cin >> val;
        idx[val] = i;
    }

    for(int i = 0; i<n; i++)
        cin >> b[i];

    g[1] = idx[b[0]];
    int len = 1;

    for(int i = 1; i<n; i++)
    {
        if(idx[b[i]] > g[len])
        {
            g[++len] = idx[b[i]];
        }
        else
        {
            int loc = lower_bound(g+1,g+len,idx[b[i]]) - g;
            g[loc] = idx[b[i]];
        }
    }

    cout << len;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值