關於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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM