最长公共子序列(LCS)

定义:

最长公共子序列,英文缩写为LCS(Longest Common Subsequence)。其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。而最长公共子串(要求连续)和最长公共子序列是不同的


例如  下面两个单词中颜色标记出来的 data 就是 didactical  advantage 的 最长公共子序列, 长度为4;

didactical
advantage 


求解:

一、首先我们用递归思想设计一个正确、可行的解。

①如果其中一个序列为空,那么最长公共子序列一定为空,长度为 0 。

②我们比较这两个序列的最后一个字母,如果相同,我们就可以将可以将问题规模缩小1位。

例如:

appl                                                                     appl

abstpdple  最后一个相同 , 我们就可以进而求     abstpdpl 的最长公共子序列, 当然这时候要记录下已经有了的公共部分。


③如果最后一位不同怎么办?

例如

ap

abstpd , 可以想到 ,这时候问题分为了两个部分:

        a                                                ap

求解 abstpd 的最长公共子序列 ,和 abstpd 的最长公共子序列

因为最后一个字符不一样,所以我们必定要舍弃两个序列中其中一个的最后一位。显然,我们需要这两个问题中公共子序列更长的那一个。


这样,整个递归算法就算描述完了。


实现的代码在文章最后给出,最好自己直接按照上述描述敲出来代码。


二、寻找优化:动态规划的思想。

我们可以简单的对上面递归的算法进行一些时间分析

最好的情况,也就是不出现③中情况的时候,只需要O(min(lena,lenb)).

然而一旦出现③,那么问题就会分解为两个问题,更糟糕的是,这两个问题的子问题可能雷同!

如图可以看到,两个子问题的子问题可能雷同,将造成大量的重复计算。

最坏的情况将达到 C(lena,lena+lenb)(组合数符号), 当lena == lenb 时 ,大致为 O(2^n)。

时间复杂度达到了指数级别!


这里我们运用动态规划的思想,刚才递归是自顶向下,我们自底向上的去求解问题。

首先初始化dp数组的第一行和第一列为0;




如图所示,我们可以一行一行的填写这张表,运用上面递归中所讲的方法,

如果遇到相同的字符,dp[i][j] = dp[i-1][j-1] + 1 ,

如果遇到不同的字符 dp[i][j] =max(dp[i-1][j] , dp[i][j-1] ) .

这样每个空格只用遍历一次就能求出所有的结果,dp[lena][lenb] 就是结果啦。


源代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 600;
int dp[maxn][maxn];
//递归版本的LCS
int fun(string a,string b,int count){
	int lena = a.length();
	int lenb = b.length();
	if(lena == 0 || lenb == 0)
		return count;
	string tmpa,tmpb;
	tmpa = a.substr(0,lena-1);
	tmpb = b.substr(0,lenb-1);
	if(a[lena-1] == b[lenb-1]){
		return fun(tmpa,tmpb,count+1);
	}
	else {
		return max(fun(tmpa,b,count),fun(a,tmpb,count));
	}
}
//动态规划LCS
void dynamic(string a,string b){
	int lena = a.length();
	int lenb = b.length();
	for(int i=0;i<=lena;i++){
		dp[i][0] = 0;
	}
	for(int i=0;i<=lenb;i++){
		dp[0][i] = 0;
	}
	for(int i=0;i<lena;i++){
		for(int j=0;j<lenb;j++){
			if(a[i] == b[j]){
				dp[i+1][j+1] = dp[i][j] + 1;
			}
			else{
				dp[i+1][j+1] = max(dp[i+1][j] , dp[i][j+1]);
			}
		}
	}
	printf("%d\n",dp[lena][lenb]);
}
void print(){//打印dp数组
	for(int i=0;i<=lena;i++){
		for(int j=0;j<=lenb;j++){
			printf("%d ",dp[i][j]);
		}
		puts("");
	}
}
int main(){
	string a,b;
	while(getline(cin,a)){
		getline(cin,b);
		dynamic(a,b); //动态规划
		// int ans = fun(a,b,0); //递归版本
		// printf("%d\n",ans);
	}
	return 0;
}






posted @ 2015-09-22 12:58  编程菌  阅读(440)  评论(0编辑  收藏  举报