zoukankan      html  css  js  c++  java
  • 最长公共子序列问题LCS

    最长公共子序列问题

    在这里介绍一种在动态规划中类似于板子题的类型 : 最长公共子序列问题。(Link

    首先来看题面:给出1-n的两个排列P1和P2,求它们的最长公共子序列。

    我们看到题之后的第一个想法肯定就是一个O(n^2) 的DP,但是看到数据:

    对于(100)%的数据,(n≤100000)

    那么我们知道肯定是过不了的了,那么我们考虑一个(O(nlogn))的DP方法。

    首先构造一个(K[MAXN])数组,这个数组的用处是用来记录一个数字在字串S1中的位置。因为S1和S2都是由1到n的排列,那么这个位置肯定是唯一的。

    然后我们结合样例分析。

    (S1) : {(3, 2, 1, 4, 5)} ; (S2) : {(1, 2, 3, 4, 5)} ; 当然答案很显然是(3)。也就是{(3, 4, 5)}或者{(2, 4, 5)}

    当前的(K)对于每一个(S2)中的数的数组就是:{(3, 2, 1, 4, 5)}。那么我们考虑这个一个事情:如果(S2)序列的每一个元素如果在(S1)序列中的位置是递增的,那么也就说明(S1)中的数在(S2)中都偏后。那么我们就可以将问题转变为求(K)数组中的最长上升子序列

    那么问题来了,最长上升子序列怎么求呢?

    当然考虑(DP),那么我们可以显而易见地得出一个(O(n^2))的算法:设(F[i][j])为第一个序列中前i个元素和第j和序列中前j个元素中的最长上升子序列的长度是多少。那我们对于每一个枚举到的(i),再枚举在(i)之前的所有元素(j),如果(Data[j] < Data[i])并且(Dp[j] + 1 > Dp[i]),那么我们就让(Dp[i])继承(Dp[j])然后再加上(1)

    [Dp[i] = max_{j = 1}^{j <= i}(Dp[i], Dp[j] + 1) ]

    void LIS(){
        for(int i = 1; i <= N; i ++)
    	    for(int j = 1; j <= i; j ++)
    	        if(Data[j] < Data[i])
    	            Dp[i] = max(Dp[i], Dp[j] + 1) ;
    }
    

    但是我们会发现这种做法并不可行因为它的时间复杂度仍然是(O(n^2)),所以我们考虑使用一个(F[i])表示长度为(i)的最长上升子序列的最后一位的最小值是多少。记这个玩意干什么呢?你想,你现在有一个最长上升子序列的长度为(i),最后一位的值是(T),那么当你选择下一位(i + 1)的时候,你的选择范围只有([T, max_{i = 1}^{i <= N}Data[i]]),那么你的T越小,可以选择的范围就越大。运用这个贪心的思想,我们可以发现在第二次枚举的时候,我们就可以二分查找了。

    //LIS (Longest Rising Subsequence)
    int F[MAXN] ;
    void LIS(){
    	for(int i = 1; i <= N; i ++){
    		Data[i] = Read() ;
    		F[i] = Inf ;
    	}
    	F[1] = Data[1] ; int Tot = 1 ;
    	for(int i = 2; i <= N; i ++){
    		int L = 0, R = Tot ;
    		if(Data[i] > F[Tot]) F[++ Len] = Data[i] ;
    		else{
    			while(L < R){
    			// 因为F[j]的值一定是递增的,所以可以二分查找第一个小于等于Data[i]的点
    				if(F[Mid] > Data[i])
    					R = Mid ;
    				else L = Mid + 1 ;
    			}
    			F[L] = min(F[L], Data[i]) ;
    		}
    	}
    // Ans : Tot ;
    }
    

    那么我们也就得出了最长上升子序列的问题的解。

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <vector>
    #include <queue>
    #include <cmath>
    #include <map>
    #define For1(i, A, B) for(register int i = (A), i##_end_ = (B); i <= i##_end_; ++ i)
    #define For2(i, A, B) for(register int i = (A), i##_end_ = (B); i >= i##_end_; -- i)
    #define MEM(Now, K) memset(Now, K, sizeof(Now))
    #define CPY(Now, K) memcpy(Now, K, sizeof(Now))
    #define Debug(Now) (cerr << Now << endl)
    #define Min(A, B) (A < B ? A : B)
    #define Max(A, B) (A < B ? B : A)
    #define Mid ((L + R) >> 1)
    #define SCPY(A, B) strcpy(A, B)
    #define Inf 0x7fffffff
    #define RE register
    #define IL inline
    #define MAXN 100010
    #define MAXM 100010
    #define _X first
    #define _Y second
    using namespace std ;
    
    typedef unsigned long long ULL ;
    typedef pair<long long, int>  PLI;
    typedef pair<int, int> PII;
    typedef unsigned int UINT;
    typedef long double LDB;
    typedef long long LL ;
    typedef double DB ;
    
    IL int Read(){
    	int X = 0, F = 1 ;  char ch = getchar() ;
    	while(ch < '0' || ch > '9'){ if(ch == '-') F = - 1 ; ch = getchar() ; }
    	while(ch <= '9' && ch >= '0') X = (X << 1) + (X << 3) + (ch ^ 48), ch = getchar() ;
    	return X * F ;
    }
    IL double DBRead(){
        double X = 0, Y = 1.0 ; int W = 0 ; char ch = 0 ;
        while(! isdigit(ch)) { W |= ch == '-' ; ch = getchar() ; }
        while(isdigit(ch)) X = X * 10 + (ch ^ 48), ch = getchar() ;
        ch = getchar();
        while(isdigit(ch)) X += (Y /= 10) * (ch ^ 48), ch = getchar() ;
        return W ? - X : X ;
    }
    IL void Print(int X){
         if(X < 0) putchar('-'), X = - X ;
         if(X > 9) Print(X / 10) ; putchar(X % 10 + '0') ;
    }
    int N, S1[MAXN], S2[MAXN], F[MAXN], K[MAXN] ;
    int LCS(){ // Return the lenth of LCS
    	N = Read() ;
    	for(int i = 1; i <= N; i ++){
    		S1[i] = Read() ;
    		K[S1[i]] = i ;
    	}
    	for(int i = 1; i <= N; i ++){
    		S2[i] = Read() ;
    		F[i] = Inf ;
    	}
    	int Tot = 0 ; F[0] = 0 ;
    	for(int i = 1; i <= N; i ++){
    		int L = 0, R = Tot ;
    		if(K[S2[i]] > F[Tot]) F[++ Tot] = K[S2[i]] ;
    		else{
    			while(L < R){
    				if(F[Mid] > K[S2[i]])
    					R = Mid ;
    				else L = Mid + 1 ;
    			}
    			F[L] = min(F[L], K[S2[i]]) ;
    		}
    	}
    	return Tot ;
    }
    int main() {
    	Print(LCS()) ;
    	return 0 ;
    }
    
    
    
  • 相关阅读:
    mysql使用group by查询报错SELECT list is not in GROUP BY clause and contains nonaggregated column...解决方案
    CentOS7 使用minikube 搭建kubernetes 学习环境
    5
    4
    3
    2
    1
    8
    7
    Algorithm
  • 原文地址:https://www.cnblogs.com/sue_shallow/p/LCS.html
Copyright © 2011-2022 走看看