最长上升子序列 最长公共子序列

最长上升子序列为DP问题,所以我们可以用DP来解决。它有两种算法,首先介绍一下
时间复杂度为O(n^2):
递推关系:dp[i]={1,d[j]+1|j<i且aj<ai}
dp[]:=以ai为末尾的最长上升子序列的长度
而以ai结尾的上升子序列是:
(1)只包含ai的子序列
(2)在满足j<i并且aj<ai的以aj为结尾的上升子列末尾,追加上ai后得到的子序列。
我们依次遍历整个序列,每一次求出从第一个数到当前这个数的最长上升子序列,直至遍历到最后一个数字为止,然后再取dp数组里最大的那个即为整个序列的最长上升子序列。我们用dp[i]来存放序列1-i的最长上升子序列的长度,那么dp[i]=max(dp[j])+1,(j∈[1, i-1]); 显然dp[1]=1,我们从i=2开始遍历后面的元素即可。
代码如下:

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <cstring>
using namespace std;

const int N=1e6;
int n,a[N],dp[N];


void f()  //求最长上升子序列
{
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        dp[i]=1;
        for(int j=1;j<=n;j++)
            if(a[j]<a[i])
              dp[i]=max(dp[i],dp[j]+1);
        ans=max(dp[i],ans);
    }
    cout<<ans<<endl;
}

int main()
{
    memset(a,0,sizeof(a));
    memset(dp,0,sizeof(dp));
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    f();
    return 0;
}

时间复杂度为O(nlogn):
必需满足条件:dp数列中除INF之外是单调递增的
最开始初始话dp为inf。然后由前向后逐个考虑数列的元素,对于每个aj,如果i=0或者dp[i-1]<aj的话,就用dp[i]=min(dp[i],aj)进行更新,最终找出使得dp[i]

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <cstring>
using namespace std;

const int N=1e6;
const int inf=0x3f3f3f3f;
int n,a[N],dp[N];


void f()  //lower_bound()为二分函数,详情请见二分法那篇博客
{
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        *lower_bound(dp+1,dp+n+1,a[i])=a[i];
    }
    cout<<lower_bound(dp+1,dp+n+1,inf)-dp-1<<endl;
}

int main()
{
    memset(a,0,sizeof(a));
    memset(dp,inf,sizeof(dp));
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    f();
    return 0;
}

另一种二分:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define MAXN 40005
using namespace std;
int arr[MAXN],ans[MAXN],len;
int main()
{
    int n;
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n;
        for(int i=1; i<=n; ++i)
            scanf("%d",&arr[i]);
        ans[1] = arr[1];
        len=1;
        for(int i=2; i<=n; ++i)
        {
            if(arr[i]>ans[len])
                ans[++len]=arr[i];
            else
            {
                int pos=lower_bound(ans,ans+len,arr[i])-ans;
                ans[pos] = arr[i];
            }
        }
        cout<<len<<endl;
    }
    return 0;
}

最长公共子序列
引进一个二维数组dp[][],用dp[i][j]记录s[i]与t[j] 的LCS 的长度.
我们是自底向上进行递推计算,那么在计算dp[i,j]之前,dp[i-1][j-1],dp[i-1][j]与dp[i][j-1]均已计算出来。此时我们根据s[i] = t[j]还是s[i] != t[j],就可以计算出dp[i][j]。

问题的递归式写成:这里写图片描述
回溯输出最长公共子序列过程:
这里写图片描述

首先做初始化。将c[0][i]和从c[i][0]初始化为0,然后一行一行的填表。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int N=1e6;
int dp[500][500];
char s[1000],t[1000];
int n,m;

void f()
{
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        if(s[i]==t[j])
            dp[i+1][j+1]=dp[i][j]+1;
        else
            dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
    cout<<dp[n][m]<<endl;
}


int main()
{
    scanf("%s%s",s,t);
    n=strlen(s);
    m=strlen(t);
    f();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值