动态规划之最长公共子序列问题 C++实现

动态规划之最长公共子序列问题 C++实现

原理

在之前的文章当中,作者论述了什么是动态规划,这次,我们来看看,如何用动态规划解决最长公共子序列问题。

这个问题经常运用在判断两种生物的相似度----DNA比对上。对比俩串的方式有很多种,例如如果一个串是另一个的字串,那么可以说两个串是相似的:如果将一个串转换为另一个串的操作很少,那么也可以说这两个串是相似的。另一种衡量俩串 S 1 , S 2 S_1,S_2 S1,S2的相似度方式为:寻找第3个串 S 3 S_3 S3,它的所有元素也都出现在 S 1 S_1 S1 S 2 S_2 S2中,且在三个串中出现的顺序都相同,但在 S 1 , S 2 S_1,S_2 S1,S2中不要求连续出现。我们将最后一种相似的概念描命名最长公共子序列问题。

其形式化定义如下:给定一个序列 X = ⟨ x 1 , x 2 … , x m ⟩ X=\left\langle x_1,x_2\dots,x_m\right\rangle X=x1,x2,xm,另一个序列 Y = ⟨ y 1 , y 2 , … , y k ⟩ Y=\left\langle y_1,y_2,\dots,y_k\right\rangle Y=y1,y2,,yk满足如下条件时成为 X X X的子序列(sunsequence),即存在一严格递增的 X X X的下标序列 ⟨ i 1 , i 2 , … , i k ⟩ \left\langle i_1,i_2,\dots,i_k\right\rangle i1,i2,,ik,对所有 j = 1 , 2 , … , k j=1,2,\dots,k j=1,2,,k满足 i j = z j _{i_j}=z_j ij=zj。例如, Z = ⟨ B , C , D , B ⟩ Z=\left\langle B,C,D,B\right\rangle Z=B,C,D,B X = ⟨ A , B , C , B , D , A , B ⟩ X=\left\langle A,B,C,B,D,A,B\right\rangle X=A,B,C,B,D,A,B的子序列,对应的下标为 ⟨ 2 , 3 , 5 , 7 ⟩ \left\langle 2,3,5,7\right\rangle 2,3,5,7
给定一个序列 X = ⟨ x 1 , x 2 … , x m ⟩ X=\left\langle x_1,x_2\dots,x_m\right\rangle X=x1,x2,xm,对 i = 1 , 2 , … , k i=1,2,\dots,k i=1,2,,k,定义 X X X的第 i i i前缀为 X i = ⟨ x 1 , x 2 … , x i ⟩ X_i=\left\langle x_1,x_2\dots,x_i\right\rangle Xi=x1,x2,xi

刻画最长公共子序列的特征

L C S LCS LCS的最优子结构:令 X = ⟨ x 1 , x 2 … , x m ⟩ X=\left\langle x_1,x_2\dots,x_m\right\rangle X=x1,x2,xm Y = ⟨ y 1 , y 2 , … , y n ⟩ Y=\left\langle y_1,y_2,\dots,y_n\right\rangle Y=y1,y2,,yn为两个序列, Z = ⟨ z 1 , z 2 , … , z k ⟩ Z=\left\langle z_1,z_2,\dots,z_k\right\rangle Z=z1,z2,,zk X X X Y Y Y的任意 L C S LCS LCS
1.如果 x m = y n x_m=y_n xm=yn,则 z k = x m = y n z_k=x_m=y_n zk=xm=yn Z k − 1 Z_{k-1} Zk1 X m − 1 X_{m-1} Xm1 Y n − 1 Y_{n-1} Yn1的一个 L C S LCS LCS
2.如果 x m ≠ y n x_m\neq y_n xm=yn,则 z k ≠ x m z_k\neq x_m zk=xm Z Z Z X m − 1 X_{m-1} Xm1 Y Y Y的一个 L C S LCS LCS
3.如果 x m ≠ y n x_m\neq y_n xm=yn,则 z k ≠ y n z_k\neq y_n zk=yn Z Z Z X X X Y n − 1 Y_{n-1} Yn1的一个 L C S LCS LCS

一个递归解

我们定义 c [ i , j ] c[i,j] c[i,j]表示 X i X_i Xi Y j Y_j Yj L C S LCS LCS的长度。则根据 L C S LCS LCS问题的最优子结构性质,可得如下公式:
c [ i , j ] = { 0 i f   i = 0   o r   j = 0 c [ i − 1 , j − 1 ] + 1 i f   i , j > 0   a n d   x i = y j m a x ( c [ i , j − 1 ] , c [ i − 1 , j ] ) i f   i , j > 0   a n d   x i ≠   y j c[i,j]=\left\{\begin{matrix} 0&if\ i=0\ or\ j=0 \\ c[i-1,j-1]+1&if\ i,j>0\ and\ x_i=y_j \\ max(c[i,j-1],c[i-1,j])&if\ i,j>0\ and\ x_i\neq\ y_j \end{matrix}\right. c[i,j]=0c[i1,j1]+1max(c[i,j1],c[i1,j])if i=0 or j=0if i,j>0 and xi=yjif i,j>0 and xi= yj

源代码

#include <iostream>
#include <utility>
#include <vector>
#include <string>

using namespace  std;

//ACCGTCGAGTGCGCGGAAGCCGGCCGAA & CTCGTTCGGAATGCCGTTGCTCTGTAAA
string temp_strX = { "#ACCGTCGAGTGCGCGGAAGCCGGCCGAA" }, temp_strY = { "#CTCGTTCGGAATGCCGTTGCTCTGTAAA" }; 

//Memoized of Lcs
pair<vector<vector<int>>,vector<vector<int>>> Lcs_Length(const string &temp_strX, const string &strY) {
	auto temp_m = temp_strX.size() - 1, temp_n = temp_strY.size() - 1;
	vector<vector<int>> temp_VecB, temp_VecC;

	temp_VecB.resize(temp_m + 1);
	temp_VecC.resize(temp_m + 1);

	for(auto &i : temp_VecB) {
		i.resize(temp_n + 1);
	}
	for(auto &i : temp_VecC) {
		i.resize(temp_n + 1);
	}

	for(auto i = 1; i <= temp_m; ++i) {
		temp_VecC[i][0] = 0;
	}
	for(auto j = 0; j <= temp_n; ++j) {
		temp_VecC[0][j] = 0;
	}

	for(auto i = 1; i <= temp_m; ++i) {
		for(auto j = 1; j <= temp_n; ++j) {
			if(temp_strX[i] == temp_strY[j]) {
				temp_VecC[i][j] = temp_VecC[i - 1][j - 1] + 1;
				temp_VecB[i][j] = -1;
			}
			else if(temp_VecC[i - 1][j] >= temp_VecC[i][j - 1]) {
				temp_VecC[i][j] = temp_VecC[i - 1][j];
				temp_VecB[i][j] = -2;
			}
			else {
				temp_VecC[i][j] = temp_VecC[i][j - 1];
				temp_VecB[i][j] = -3;
			}
		}
	}

	return make_pair(temp_VecC, temp_VecB);
}

//Print
void Print_Lcs(const vector<vector<int>> & temp_VecB, const string &temp_strX, const size_t &i, const size_t &j) {
	if(i == 0 || j == 0) {
		return;
	}

	if(temp_VecB[i][j] == -1) {
		Print_Lcs(temp_VecB, temp_strX, i - 1, j - 1);
		cout << temp_strX[i];
	}
	else if(temp_VecB[i][j] == -2) {
		Print_Lcs(temp_VecB, temp_strX, i - 1, j);
	}
	else {
		Print_Lcs(temp_VecB, temp_strX, i, j - 1);
	}
}

int main() {
	auto temp_pair = Lcs_Length(temp_strX, temp_strY);
	Print_Lcs(temp_pair.second, temp_strX, temp_strX.size() - 1, temp_strY.size() - 1);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值