最长上升子序列(LIS) -最长公共子序列(LCS)

本文介绍了求解最长上升子序列(LIS)及最长公共子序列(LCS)问题的算法实现,包括O(n^2)的时间复杂度方法及LIS的优化O(nlogn)算法,并给出具体的状态转移方程和代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们可以发现,求整个序列的最长上升子序列长度的子问题是“求以ak(k=1, 2, 3…N)为终点的最长上升子序列的长度”。

我们用max_len(k)表示以ak作为终点的最长上升子序列的长度。

初始状态:max_len(1)=1;
状态转移方程:max_len(k) = max{max_len(i) 1<=i < k 且 ai < ak且 k≠1 } + 1;
若没有符合条件的i,则max_len(k) = 1;

ak左边任何终点小于ak的子序列,加上ak自身后就能形成一个更长的上升子序列。

我们还使用了algorithm中的max_element函数,它的作用是返回容器中最大值。(min_element返回容器中最小值)

代码实现如下:

#include <iostream>
#include <algorithm>

using namespace std;
const int M =1000;
int a[M],max_len[M];

int main()
{
    int n;
    cin >>n;
    for(int i=1; i<=n; i++)  //  输入数据并把初始长度都设为1
    {
        cin >> a[i];
        max_len[i]=1;
    }
    for(int i=2; i<=n; i++)   //求以第i个数为终点的最长上升子序列的长度
    {
        for(int j=1; j<i; j++)  //查看以第j个数为终点的最长上升子序列
        {
            if(a[i] > a[j])
                max_len[i] = max(max_len[i], max_len[j]+1);
        }
    }
    cout << *max_element(max_len+1, max_len+n+1); // 输出最大元素
    return 0;
}

加一个nlogn的求lis的方法,主要就是利用了二分的思想。

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 40010;

int a[MAXN];

bool cmp(int x, int y)
{
    return x<y;
}

//int binary_search(int i)
//{
//    int left,right,mid;
//    left=0,right=len;
//    while(left<right)
//    {
//        mid = left+(right-left)/2;
//        if(ans[mid]>=arr[i])
//            right=mid;
//        else
//            left=mid+1;
//    }
//    return left;
//}
int main()
{
    int cas, n, ans;
    scanf("%d",&cas);
    while(cas--)
    {
        scanf("%d",&n);
        ans = 0;
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
            int *p = lower_bound(a,a+ans,a[i],cmp);
            if(p == a+ans)
                ans++;
            *p = a[i];
        }
        printf("%d\n",ans);
    }
    return 0;
}

最长公共子序列(LCS)
定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。而最长公共子串(要求连续)和最长公共子序列是不同的。(百度百科)
在看了很多人写的博客后,我觉得还是好好理解状态转移方程,然后自己写一遍,debug一遍,体会更深。
(想写比这个时间复杂度低的算法,可以看下这个https://www.zhihu.com/question/21974937)。
杭电1159有练习题。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>    //最长公共子序列   hdu1159 已AC

using namespace std;
int c[1000][1000];

int main()
{
    char a[1000],b[1000];
    int len1,len2;
    while(scanf("%s%s",a,b) != EOF)
    {
        memset(c,0,sizeof(c));
        len1 = strlen(a);
        len2 = strlen(b);
        for(int i=1; i<=len1; i++)
        {
            for(int j=1; j<=len2; j++)
            {
                if(a[i-1] == b[j-1])
                    c[i][j] = c[i-1][j-1] + 1;     //状态转移方程
                else
                    c[i][j] = max(c[i][j-1],c[i-1][j]);
            }
        }
        cout <<c[len1][len2]<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值