关于LIS和LCS问题的o(nlogn)解法

o(n^2)解法就不赘述了,直接解释o(nlogn)解法

LIS最长递增子序列;

先明确一个结论:在长度最大为len的递增序列里若末尾元素越小,该递增序列越容易和后面的子序列构造出一个更长的递增子序列。也即认为,长度为len的递增子序列中末尾元素最小的那种最需要保留。我们不妨称这个目前找到序列为到目前为止的 最优序列。

因此设置一个数组lis[i]其中 i 表示此时最大递增序列的长度,数组值表示此时达到 i 的最优序列(也即 长度为len的递增子序列中末尾元素最小的那种)的末尾元素。

那么此时只需遍历一遍输入数据,维护lis的上述特性,则最后所得的lis数组的长度就是要求的len。

不多言,结合代码理解:

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=1e5+5;
int a[maxn];
int n;
int lis[maxn];
int len=1;
int find(int x){
    int l=1,r=len,m;
    while(l<r){
        m=l+(r-l)/2;
        if(lis[m]>=a[x]){//这里若去掉等号即为 非严格递增序列
            r=m;
        }
        else{
            l=m+1;
        }
    }
    return l;
}
int main(void){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    lis[1]=a[1];
    for(int i=2;i<=n;i++){
        if(a[i]>lis[len]){
            lis[++len]=a[i];
        }
        else{
            int pos=find(i);
            lis[pos]=a[i];
        }
    }
    printf("%d",len);
    return 0;
}

LCS最长公共子序列:

最长公共子序列 的 nlogn 的算法本质是 将该问题转化成 最长增序列(LIS),因为 LIS 可以用nlogn实现,所以求LCS的时间复杂度降低为 nlogn。

假设有两个序列 s1[ 1~6 ] = { a, b, c , a, d, c }, s2[ 1~7 ] = { c, a, b, e, d, a, b }。

记录s1中每个元素在s2中出现的位置, 再将位置按降序排列, 则上面的例子可表示为:

loc( a)= { 6, 2 }, loc( b ) = { 7, 3 }, loc( c ) = { 1 }, loc( d ) = { 5 }。

将s1中每个元素的位置按s1中元素的顺序排列成一个序列s3 = { 6, 2, 7, 3, 1, 6, 2, 5, 1 }。

在对s3求LIS得到的值即为求LCS的答案。(这点我也只是大致理解,读者可以自己理解甚至证明。)

这里给出全排列情况下的代码(即两个序列长度相同,数字组成相同,无重复元素)

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
const int maxn=1e6+5;
int n,len=0;
int lis[maxn];
int a[maxn];
int b[maxn];
int loc[maxn];
int find(int x){
    int l=1,r=len,m;
    while(l<r){
        m=l+(r-l)/2;
        //if(lis[m]>=b[x]){//智障错误,找了那么久。。
        if(lis[m]>=x){
            r=m;
        }
        else  l=m+1;
    }
    return l;
}
int main(void){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){
        scanf("%d",&b[i]);
        loc[b[i]]=i;
    }
    for(int i=1;i<=n;i++){
        b[i]=loc[a[i]];
    }
//    for(int i=1;i<=n;i++)printf("%d",b[i]) ;//
//    printf("\n");
    if(n!=0)lis[++len]=b[1];
    for(int i=2;i<=n;i++){
        if(b[i]>lis[len]){
            lis[++len]=b[i];
        }
        else{
            int pos=find(b[i]);
            lis[pos]=b[i];
        }
    }
    printf("%d",len);
    return 0;
}

原文地址:https://www.cnblogs.com/KYSpring/p/9021909.html

时间: 2024-08-06 04:30:04

关于LIS和LCS问题的o(nlogn)解法的相关文章

浅谈LIS、LCS、LCIS之LIS

首先定义一下LIS问题:给定一个长度为n的序列a,求它的最长上升子序列的最大长度. 方法一:(n^2). 令f[i]为以i结尾的最长上升子序列的长度,当且仅当j满足a[j]<a[i](1≤j≤i≤n)时,f[i]从f[j]转移而来. 这个状态转移方程易构造:f[j] = MAX(f[i]) + 1(a[j] < a[i]) . 方法二:(n log n) 现在,我们仔细考虑计算f[i]时的情况. 假设有两个元素a[j1]和a[j2],满足(1)0<j1<j2<i (2)a[j

hdu 5087 Revenge of LIS II lcs变形

点击打开链接链接 Revenge of LIS II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1028    Accepted Submission(s): 334 Problem Description In computer science, the longest increasing subsequence proble

动态规划——背包、LIS、LCS

问题 A: 导弹拦截 时间限制: 1 Sec  内存限制: 128 MB 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意 的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所 有的导弹.输入导弹一次飞来的高度(雷达给出的高度不大于30000的正整数).计算这套系统最多能拦截多少导弹. 输入 n颗依次飞来的导弹高度,导弹颗数<=1

最长上升子序列 O(nlogn)解法 (转)

转自 http://www.cppblog.com/mysileng/archive/2012/11/30/195841.html 最长递增子序列问题:在一列数中寻找一些数,这些数满足:任意两个数a[i]和a[j],若i<j,必有a[i]<a[j],这样最长的子序列称为最长递增子序列. 设dp[i]表示以i为结尾的最长递增子序列的长度,则状态转移方程为: dp[i] = max{dp[j]+1}, 1<=j<i,a[j]<a[i]. 这样简单的复杂度为O(n^2),其实还有更

toj 4071 最长不下降子序列nlogn解法

题目描述:给出2D空间中的n只鸟的坐标,射手对其进行射击,要求射击的鸟的坐标越来越大,即对于第i和第i+1只鸟,要求满足:xi<=xi+1 && yi <= yi+1.求最多能射击多少只鸟. 思路:将所有点按照x坐标排序,x坐标相同则按照y坐标排序.则x方向上可以满足限制,对y方向上求最长不下降子序列即可.由于数据量较大,需要采取nlogn的优化算法. 1 #include <algorithm> 2 #include <iostream> 3 #inc

hdu4521 小明系列问题——小明序列(LIS变种 (线段树+单点更新解法))

链接: huangjing 题目:中文题目 思路: 这个题目如果去掉那个距离大于d的条件,那么必然是一个普通的LIS,但是加上那个条件后就变得复杂了.用dp的解法没有看懂,我用的线段树的解法...就是采用延迟更新的做法,用为距离要大于d啊,所以我们在循环到第i的时候,就对(i-d-1)这个点进行更新,因为如果在(i-d-1)这个点更新了,会对后面的造成影响,然后线段树的tree[]数组存的是以i结尾的最长lis,那么每次询问的时候就找最大的tree[]就可以了... 代码: 小明系列问题--小明

【LIC】O(nlogn)解法

[LIC--最长递增子序列问题] 在一列数中寻找一些数,这些数满足:任意两个数a[i]和a[j],若i<j,必有a[i]<a[j],这样最长的子序列称为最长递增子序列. O(nlogn)算法:所需要的数组 1.数组T 2.增设一个minT[]数组,minT[x]存放长度为x的最长上升子序列的最小末尾数. 3.dp[i],从一到元素T[i]结尾的最长上升子序列的长度: 具体原理转自网络: 设 T[t]表示序列中的第t个数,dp[t]表示从1到t这一段中以t结尾的最长上升子序列的长度,初始时设dp

最长上升子序列nlogn解法详解 poj 2533

先上一个n^2的算法: 1 #include <iostream> 2 using namespace std; 3 4 const int N = 1000; 5 int a[N]; 6 int g[N]; 7 8 int main () 9 { 10 int n; 11 while ( cin >> n ) 12 { 13 for ( int i = 0; i < n; i++ ) 14 { 15 cin >> a[i]; 16 g[i] = 1; 17 }

DP练习 最长上升子序列nlogn解法

openjudge 百练 2757:最长上升子序列 总时间限制:  2000ms 内存限制:  65536kB 描述 一个数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的.对于给定的一个序列(a1, a2, ..., aN),我们可以得到一些上升的子序列(ai1, ai2, ..., aiK),这里1 <= i1 < i2 < ... < iK <= N.比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上