最长公共子序列(LCS)

最长公共子序列(LCS)

给出两序列 v 1 , v 2 v1,v2 v1,v2,求它们最长公共子序列长度(子序列可以不连续)

长度问题

DP( O ( n 2 ) O(n^2) O(n2))

闫氏DP分析法

注: v 1 , v 2 v1,v2 v1,v2下标从 0 0 0开始, d p dp dp下标从1开始

  • 状态表示

    • 集合: d p [ i ] [ j ] dp[i][j] dp[i][j]:表示以 [ 1 , i ] [1,i] [1,i]区间内的 v 1 v1 v1 [ 1 , j ] [1,j] [1,j]区间内的 v 2 v2 v2最长公共子序列长度, i n i t ( d p [ i ] [ 0 ] , d p [ 0 ] [ i ] ) = 0 init(dp[i][0],dp[0][i])=0 init(dp[i][0],dp[0][i])=0
    • 属性: M a x Max Max
  • 状态计算

    • v 1 [ i ] = v 2 [ j ] v1[i]=v2[j] v1[i]=v2[j]时:继承自 L C S ( v 1 [ i − 1 ] , v 2 [ j − 1 ] ) LCS(v1[i-1],v2[j-1]) LCS(v1[i1],v2[j1]),并加1。 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j]=dp[i-1][j-1]+1 dp[i][j]=dp[i1][j1]+1
    • v 1 [ i ] ≠ v 2 [ j ] v1[i]\ne v2[j] v1[i]=v2[j]时:
      • 考虑 v 1 v1 v1回退一步:继承自 L C S ( v 1 [ i − 1 ] , v [ j ] ) LCS(v1[i-1],v[j]) LCS(v1[i1],v[j]) d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j]=dp[i-1][j] dp[i][j]=dp[i1][j]
      • 考虑 v 2 v2 v2回退一步:继承自 L C S ( v 1 [ i ] , v 2 [ j − 1 ] ) LCS(v1[i],v2[j-1]) LCS(v1[i],v2[j1]) d p [ i ] [ j ] = d p [ i ] [ j − 1 ] dp[i][j]=dp[i][j-1] dp[i][j]=dp[i][j1]
  • 状态转移方程式:

dp[i][j] = 
\left\{
\begin{array}\\
dp[i-1][j-1]+1 & ,v1[i]=v2[j] \\
max(dp[i-1][j],dp[i][j-1]) & ,\text{v1[i]}\ne \text{v2[j]}
\end{array}
\right.
extern vector<int>v1,v2;//v1 v2下标从0开始
extern vector<vector<int>>dp(v1.size()+1,vector<int>(v2.size()+1));//dp下标从1开始
int lcs(){
	for(int i=1;i<=v1.size();i++)
        for(int j=1;j<=v2.size();j++)
            if(v1[i-1]==v2[j-1]) dp[i][j]=dp[i-1][j-1]+1;
            else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
	return dp[v1.size()][v2.size()];
}
滚动数组优化
  • 交替滚动
extern vector<int>v1,v2;//v1 v2下标从0开始
extern vector<vector<int>>dp(2,vector<int>(v2.size()+1));//dp下标从1开始
int lcs(){
    int work=1,old=0;
	for(int i=1;i<=v1.size();i++){
		swap(old,work);
        for(int j=1;j<=v2.size();j++)
            if(v1[i-1]==v2[j-1]) dp[work][j]=dp[old][j-1]+1;
            else dp[work][j]=max(dp[old][j],dp[work][j-1]);
    }
	return dp[work][v2.size()];
}
  • 自我滚动

思路:定义变量 o l d old old存储 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i1][j1],且每次更新

extern vector<int>v1,v2;//v1 v2下标从0开始
extern vector<int>dp(v2.size()+1);//dp下标从1开始
int lcs(){
    for(int i=1;i<=v1.size();i++){
        int old=dp[0];//dp[0]:dp[i-1][j-1]
        for(int j=1;j<=v2.size();j++){
            int temp=dp[j];//存储更新前的dp[j]
            if(v1[i-1]==v2[j-1]) dp[j]=old+1;
            else dp[j]=max(dp[j],dp[j-1]);//dp[j]:dp[i-1][j],dp[j-1]:dp[i][j-1]
            old=temp;//将更新前的dp[j](即dp[i-1][j]赋给old,即为下一轮的dp[i-1][j-1])
        }
    }
    return dp[v2.size()];
}

最长不上升子序列求解LCS( O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n))

算法流程:设序列 v 1 , v 2 v1,v2 v1,v2,任选其中一个序列作为标准序列(此处以 v 1 v1 v1为例),用一种特殊的离散化技巧,对 v 1 v1 v1下标进行离散化,并归位到序列 v 2 v2 v2中,通过求 v 2 v2 v2的最长不上升子序列,即为 L C S ( 原 v 1 , 原 v 2 ) LCS(原v1,原v2) LCS(v1,v2)

注:此处必须求最长不上升子序列而非 L I S LIS LIS,因为需要考虑 v 1 v1 v1有重复元素情形

extern vector<pair<int,int>>v1,v2;//v1 v2下标从0开始,first代表原数据,second代表离散化后的数据
extern vector<int>d;
bool cmp(pair<int,int>a,pair<int,int>b){
    return a.first<b.first;
}
int lcs(){
    for(int i=0;i<n;i++) v1[i].second=i+1;//对v1进行离散化
    sort(v1.begin(),v1.end(),cmp);//排序,为了对v2归位能够二分加速
    for(auto &i:v2) i.second=lower_bound(v1.begin(),v1.end(),i,cmp)->second;//对v2进行归位
    for(auto i:v2){
        if(d.empty()){
            d.push_back(i.second);
            continue;
        }
        if(i.second>=d.back()) d.push_back(i.second);
        else *lower_bound(d.begin(),d.end(),i.second)=i.second;
    }
    return d.size();
}

个数问题

最长连续子串

  • 11
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值