LCS(最长公共子序列)递归/动态规划

思路来自邓俊辉—数据结构(C++)

2019/08/23
递归思路
 对于序列A[0,n] 和B[0,m], LCS(A, B)有三种情况:
  0)若n = -1或m = -1,则取作空序列(" ");
  1)若A[n] == ‘X’ == B[m],则取作LCS( A[0, n) , B[0, m) + ‘X’;
  2)若A[n] != B[m],则在LCS( A[0, n], B[0, m) ) 与LCS( A[0, n), B[0, m] ) 中取更长者;
  代码1.0(返回重复个数)

int LCS(const string& a, const string& b){   //返回公共子序列的字符个数
	int n = a.length();
	int m = b.length();
	
	if(n == 0 || m ==0)  //情况1
		return 0;
		
	else if (a.back() == b.back())  //情况2
		return LCS(a.substr(0, n - 1), b.substr(0, m-1)) + 1;
		
	else //情况3
	     return max(LCS(a.substr(0, n), b.substr(0, m - 1)),  //取两者最大值
		        LCS(a.substr(0, n - 1), b.substr(0, m)));
}

2019/08/24
  代码1.1(返回重复字符串)

string max(string A, string B){   //返回更长的字符串
	return A.length() > B.length() ? A : B;
}

string LCS(string A, string B){
	int len_A = A.length();
	int len_B = B.length();

	if( len_A == 0 || len_B == 0)   //情况1
		return "";
	else if (A.back() == B.back())  //情况2
		return LCS(A.substr(0, len_A - 1), B.substr(0, len_B - 1)) + A.back();
	else   //情况3
		return max(LCS(A.substr(0, len_A), B.substr(0, len_B - 1)),
		           LCS(A.substr(0, len_A - 1), B.substr(0, len_B)));
}

动态规划思路
   1.为什么使用动态规划:采用动态规划时,并不需要去一 一 计算那些重叠了的子问题。或者说:用了动态规划之后,有些子问题 是通过 “查表“ 直接得到的,而不是重新又计算一遍得到的。(类似Fib数列用递归方法计算时会重复计算部分项)
在这里插入图片描述
  2.如何得到dp数组
  设序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>,则:
1.若xm=yn,则zk=xm=yn且Zk-1是Xm-1和Yn-1的最长公共子序列;

2.若xm≠yn且zk≠xm ,则Z是Xm-1和Y的最长公共子序列;

3.若xm≠yn且zk≠yn ,则Z是X和Yn-1的最长公共子序列。
其中Xm-1=<x1, x2, …, xm-1>,Yn-1=<y1, y2, …, yn-1>,Zk-1=<z1, z2, …, zk-1>。
简而言之一张图(注意图有一处错误:i=0 或 j = 0时,C[i, j]不一定是0):

在这里插入图片描述
  
  3.如何利用得到dp二维数组得到公共子序列:若两序列A[i]的元素和B[i]的元素相等,那么最长公共子序列为A[0, i -1], B[0, j - 1]的最长公共子序列的值加1。否则分别是A[0, i- 1], B[0 ,j] 或者A [0, i - 1], B[0, j ]的最长公共子序列的较大值。

  代码2.0(动态规划)

int** LCS(const string a, const string b){ 
    int la = a.length();  
    int lb = b.length();
	 
    //创建动态二维数组 
    int **dp = new int *[la];
    for(int i = 0; i < la; ++i)
        dp[i] = new int[lb];
        
    //设置左、上边界的值
    for(int i = 0; i < lb; ++i)  //上
        if(a[0] == b[i])
            dp[0][i] = 1;
        else dp[0][i] = 0;
    for(int i = 0; i < la; ++i)  //左
        if(b[0] == a[i])
            dp[i][0] = 1;
        else dp[i][0] = 0;
         
    int maxium = 0; //记录最大值
    for(int i = 1; i < la; ++i)  //得到字符串a、b第i、j个字符
     for(int j =1; j < lb; ++j){
     	    if(a[i] == b[j])
     	       dp[i][j] = dp[i - 1][j - 1] + 1;
     	    else
      	       dp[i][j] = max(dp[i -1][j], dp[i][j - 1]);
      	    if(dp[i][j] > maxium)
      	        maxium = dp[i][j];
        }
        
     string result = "";  //保存公共子序列
     int k = la, t = lb;
     while (maxium >= 0) {
         if (a[k] == b[t]) {
             result += a[k];
             --k; --t;
             --maxium;
         }
         else {
             if (dp[k][t - 1] >= dp[k - 1][t])   --t;
             else --k;
         }
     }
     reverse(result.begin(), result.end());  //头文件<algorithm>
     cout << result << endl;
     return dp;
     //主函数一定要记得销毁二维动态数组所占的空间
    }

tip :创建、销毁动态二维数组

//创建动态二维数组
int **dp = new int *[la];
for(int i = 0; i < la; ++i)
	dp[i] = new int[lb];
//销毁动态二维数组
for(int i = 0; i < la; ++i){
	delete[] dp[i];
	dp[i] = NULL;
}
delete[] dp;
dp = NULL;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值