现在的位置: 首页 > 综合 > 正文

动态规划-最长公共子串LCS

2013年12月03日 ⁄ 综合 ⁄ 共 3197字 ⁄ 字号 评论关闭

1. 动态规划转移方程

两个字符串str1str2,长度分别为(len1,len2)

dp[i][j]表示以两个字符串分别以第i和第j个字符结尾所能达到的公共子序列的长度, 假设i,j 下标从1开始到len1,len2, 则

        if(str[i]=str[j])

            dp[i][j]=dp[i-1][j-1]+1;

        else

            dp[i][j]=0;

                                 0 ;                                       i = 0j= 0;

所以          dp  =        dp[i][j] = dp[i-1][j-1] + 1;     i > 0j> 0ch1[i]= ch2[j];

                                 dp[i][j]=  0;                          i > 0j> 0ch1[i]!= ch2[j];

 

2.  分析

LCS问题就是求两个字符串最长公共子串的问题。解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0。然后求出对角线最长的1序列,其对应的位置就是最长匹配子串的位置.

下面是字符串21232523311324和字符串312123223445的匹配矩阵,前者为X方向的,后者为Y方向的。不难找到,红色部分是最长的匹配子串。通过查找位置我们得到最长的匹配子串为:21232
        0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
   0 1 0 0 0 0 0 0 0 1 1 0 0 0 0
   1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
   0 1 0 0 0 0 0 0 0 1 1 0 0 0 0
   1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
   0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
   1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
   1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
   0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
   0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
   0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
   0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
   0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
但是在0和1的矩阵中找最长的1对角线序列又要花去一定的时间。通过改进矩阵的生成方式和设置标记变量,可以省去这部分时间。下面是新的矩阵生成方式:

         0 0 0 1 0 0 0 1 1 0 0 1 0 0 0
   0 1 0 0 0 0 0 0 0 2 1 0 0 0 0
   1 0 2 0 1 0 1 0 0 0 0 0 1 0 0
   0 2 0 0 0 0 0 0 0 1 1 0 0 0 0
   1 0 3 0 1 0 1 0 0 0 0 0 1 0 0
   0 0 0 4 0 0 0 2 1 0 0 1 0 0 0
   1 0 1 0 5 0 1 0 0 0 0 0 2 0 0
   1 0 1 0 1 0 1 0 0 0 0 0 1 0 0
   0 0 0 2 0 0 0 2 1 0 0 1 0 0 0
   0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
   0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
   0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
   0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
当字符匹配的时候,我们并不是简单的给相应元素赋上1,而是赋上其左上角元素的值加一。我们用两个标记变量来标记矩阵中值最大的元素的位置,在矩阵生成的过程中来判断当前生成的元素的值是不是最大的,据此来改变标记变量的值,那么到矩阵完成的时候,最长匹配子串的位置和长度就已经出来了。

【注】:

采用上述的第二种矩阵生成方式时,还可以把辅助空间开销降低到O(min(len1, len2)),其中len1和len2分别是两个字符串的长度。

具体方法如下:

令LEN=min(len1, len2),依上述第二种矩阵生成原理,从第一行(列)起,由上(左)往下(右)依次生成得到各行(列)的数值,由于每一行(列)的数据的生成只需要通过两个母串的对比和借助上一行(列)的结果得到,因此除了当前行(列)的上一行(列),之前的行(列)的数据均可以被抛弃,因此,该算法只需要用于保存上一行(列)的计算结果的长为LEN的辅助数组空间和几个额外记录单元即可。(注:对当前行(列)计算结果的保存只需要对辅助数组做更新操作即可,这个就不用说啦,呵~)

参考:http://blog.sina.com.cn/s/blog_455b20c10100929m.html

 

3. 源代码:

方法1:时间复杂度O(n1*n2),空间复杂度O(n1*n2)

//求最长公共子序列
//构造二维空间
void LCS(char *str1,char *str2){
	int len1,len2;
	int **T;
	int i,j;
	int maxlen=0;
	int end_flag=0;
	len1=strlen(str1);
	len2=strlen(str2);

	T=new int*[len1];
	for(i=0;i<len1;i++)
		T[i]=new int[len2];
	cout<<str1<<endl;
	cout<<str2<<endl;

	for(i=0;i<len1;i++){
		for(j=0;j<len2;j++){
			if(str1[i]==str2[j]){
				if(i>0 && j>0)
					T[i][j]=T[i-1][j-1]+1;// 长度为左上角元素值+1
				else 
					T[i][j]=1;

				if(maxlen<T[i][j]){
					maxlen=T[i][j];// 存储最大长度
					end_flag=i;// 存储最长公共字串的最后一个字符在str1中的位置
				}
			}
			else
				T[i][j]=0;
		}
	}

	for(i=0;i<len1;i++){//输出矩阵结果
		for(j=0;j<len2;j++){
			cout<<setw(3)<<T[i][j]<<" ";
		}
		cout<<endl;
	}
	cout<<"maxle="<<maxlen<<endl;
	if(maxlen==0)
		cout<<"no common substring"<<endl;
	else{
		for(i=end_flag-maxlen+1;i<=end_flag;i++)
			cout<<str1[i];
		cout<<endl;
	}
	for(i=0;i<len1;i++)
		delete [] T[i];
	delete T;
}

方法2:时间复杂度O(n1*n2),空间复杂度O(min(n1,n2));

//method 2:
void LCS_OPT(char *str1,char *str2){
	int len1,len2;
	int *T;
	int i,j;
	int maxlen=0;
	int end_flag=0;
	char *small=str1;
	char *big=str2;
	len1=strlen(str1);
	len2=strlen(str2);
	
	if(len1<=len2)
		T=new int[len1];
	else{
		T=new int[len2];
		small=str2;
		big=str1;
		len1=strlen(big);
		len2=strlen(small);

	}
	

	cout<<small<<endl;
	cout<<big<<endl;
	
	for(i=0;i<len1;i++){
		for(j=len2-1;j>=0;j--){
			if(small[j]==big[i]){
				if(j>0 && i>0)
					T[j]=T[j-1]+1;// 长度为左上角累计的元素值+1
				else 
					T[j]=1;
				
				if(maxlen<T[j]){
					maxlen=T[j];// 存储最大长度
					end_flag=j;// 存储最长公共字串的最后一个字符在small中的位置
				}
			}
			else
				T[j]=0;
		}
	}
	
	for(i=0;i<len2;i++){//输出结果
		cout<<setw(3)<<T[i]<<" ";
	}
	cout<<endl;

	cout<<"maxle="<<maxlen<<endl;
	if(maxlen==0)
		cout<<"no common substring"<<endl;
	else{
		for(i=end_flag-maxlen+1;i<=end_flag;i++)
			cout<<small[i];
		cout<<endl;
	}
	
}

 

 

 

 

抱歉!评论已关闭.