问题描述
在生物学应用中,经常要比较两个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)