Dynamic Programming longest common subsequence(LCS) 动态规划之最长公共子序列


问题描述

在生物学应用中,经常要比较两个DNA的相似度,一个DNA可看做是一个串。一种衡量方法是,我们说这两个DNA相似可以看做S1是S2的子串(字符串匹配问题)。第二种方法是找出第三个串S3,S3中的每一个元素都出现在S1和S2中,这就是一个求最长公共子序列的应用。

例如:

S1 =ABCD ; S2 = BECD; 那么LCS S3= BCD;


解决一群LCS的问题属于NP-hard问题,一种方法就是枚举出S1中的每一种子序列,然后去比较S2-SN中是否有这个序列,但是这种方法需要指数级的时间复杂度。但是如果是只是求两个序列的LCS,可以用动态规划的方法解决。


算法思想:

假设现在有两个sequence,S1,S2,找出他们的LCS,先将S1的最后一个元素取下来,称为E1,剩余的部分称为sub1,也就是 S1= E1+SUB1,S2 = E2+SUB2

然后观察这些部分,要求他们的LCS,会有如下几种情况。

1:E1 是LCS的一分部,E2不是,那么在S2中一定能找到一个元素与E1相等。也就是说要E2可以扔掉了,要找S1,S2的LCS,就是要找S1,sub2的LCS。

2:与第一种情况类似,E1,E2调换一下。

3:E1不等于E2,而且E1,E2都不是LCS的一部分,那么就分别再求sub1,sub2的LCS

3: E1=E2 :也就是说,他们的最后一个元素一样,那么这个问题可以就是属于减治法范畴(decrease and conquer),然后再分别求sub1和sub2的LCS再末尾加上E1就是S1和S2的LCS了。

 递归式:

求A[0,M],B[0,N]的LCS

1:当 M = -1或 N = -1 则 LCS = ‘’; //空序列

2:当 A[M] = B[N]  则 LCS = LCS(A[M-1], B[N-1] + 'A[M]'// 减治法(decrease and conquer)

3: A[N] 不等于 A[M] 则 LCS = max ( LCS(A[M], B[N-1]), LCS(A[M-1), B[N))//分治法 (divid and conquer)


事实上用递归的办法来实现,会造成很多的时间浪费,如果当N=M时比较A[0]与B[0]的次数为O(2^N)開銷會非常大


逆向迭代:


这个时候用到动态规划的思想,要用于递归的方向相反逆向迭代,从(A[0],B[0])开始比对,如果相等则取左上角的LCS数字加1,如果不相等则取左边或者上面的数字,一直循环遍历到A[n],B[m]即可得到最终的LCS,LCS数字越高就说明两段字符串相似度越高。





实现

问题说到这里可以轻松写出实现:


int n,m;
char a[maxn],b[maxm];//两个序列
int dp [maxn+1][maxm+1] = {0};//记忆化矩阵



int main(int argc , const char * argv[])
{
    for (int i = 0;  i<= n;  i++)
    {
        for(int j = 0;j <= m; j++)
        {
            if ( a[i] == b[j] )
            {
                dp[i+1][j+1]  = dp[i][j]+1;
            }
            else
            {
                dp[i+1][j+1] = max (dp[i][j+1],dp[i+1][j]);
            }
        }
    }
    
    cout<<dp[n][m];
}


时间复杂度

这里用到两个for循环,所以复杂度等于O(n*m)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值