动态规划——求解最长公共子序列问题

参考 俞征武《图解算法》第三章动态规划中求解最长相同字符串问题。

动态规划的精神:利用小问题的最优解组成大问题的最优解。

动态规划解决问题的3个步骤应该是:

(1)此问题的“大问题的最优解”可以利用“小问题”的最优解求取。

(2)利用一个数学式子将“大问题的最优解”和“小问题的最优解”之间的关系清楚地表达出来。

(3)先将“小问题的最优解”计算出后存储下来,再利用他们计算出“大问题的最优解”。

问题

最长公共子序列
问题老王找到失散多年的兄弟。为确定两人的缘关系,他决定将两人的基因进行对比,请编写一个程序比较两组基因,并找到这两组基因(字符串)共同拥有的基因组(最长相同子字符串)。注意存在这两组基因的子字符串,其先后顺序需一致。
输入

两组字符串X=<x_{1},x_{2},...,x_{m}>  Y=<y_{1},y_{2},...,y_{n}>

X=<A,T,C,T,G,A,T>
Y=<T,G,C,A, T,A>

输出

最长相同子字符串Z=<z_{1},z_{2},...,z_{k}>及其长度。此处所有

z_{i}(1\leq i\leq k)需同时出现于x和Y中,且前后出现的顺序不变

Z=<T,C,T,A>, 4

 


 

 

 

 

 

 

算法程序

package 第八章_动态规划;

import java.util.Vector;

/*
 * 计算最长公共子序列长度:
 * dp[i][j] = 0 				i=0或j=0——边界条件
 * dp[i][j] = dp[i-1][j-1]+1 			a[i-1]=b[j-1]
 * dp[i][j] = MAX(dp[i][j-1],dp[i-1][j]) 	a[i-1]!=b[j-1]
 * 
 * 求最长公共子序列subs(利用vector容器):
 * (1)当元素值等于上方相邻元素值(dp[iJ[j]=dp[i-1][j])时i减1。
 * (2)否则,当元素值等于左方相邻元素值(dp[i][i]=dp[i][j-1])时j减1.
 * (3)若元素值与上方、左边的元素值均不相等(dp[i][j]≠dp[i-1] 且 dp[i][i]≠dp[i][j-1]),
 * 说明一定有dp[i][j]=dp[i-1][j-1]+1,此时a[i-1]=b[j-1],将a[i-1]添加到subs中,并将i、j均减1。
 */

public class 最长公共子序列 {
	
	static int MAX = 51;	//序列中最多的字符个数
	//问题表示
	static int m;		//a,b序列的长度
	static int n;
	static String a,b;
	//求解结果表示
	static int dp[][] = new int[MAX][MAX];	//动态规划数组
	static Vector<String> subs= new Vector<String>();	//存放LCS(Longest Common Subsequence)最长公共子序列
	
	public static void LCSlength(char[] A, char[] B) {	//求dp
		int i,j;
		for(i=0; i<=m; i++)					//将dp[i][0]置为0,边界条件
			dp[i][0] = 0;
		for(j=0; j<n; j++)					//将dp[0][j]置为0,边界条件
			dp[0][j] = 0;
		for(i=1; i<=m; i++)					//两重for循环处理a、b的所有字符
			for(j=1; j<=n; j++) {	//情况(1)
				if(A[i-1] == B[j-1]) 
					dp[i][j] = dp[i-1][j-1]+1;		
				else		//情况(2)
					dp[i][j] = Math.max(dp[i][j-1], dp[i-1][j]);
			}
	}
	
	public static void Buildsubs(char[] A) {	//由dp构造subs
		int k = dp[m][n];			//k为a和b的最长公共子序列的长度
		int i=m;
		int j=n;
		while(k>0) {				//在subs中放入最长公共子序列(反向)
			if(dp[i][j] == dp[i-1][j])	//元素值等于上方相邻元素值
				i--;
			else if(dp[i][j] == dp[i][j-1])	//元素值等于左方相邻元素值
				j--;
			else {				//与上方、左边元素的值均不相等
				subs.add(String.valueOf(A[i-1]));//在subs中添加a[i-1]
				i--;									
				j--;
				k--;
			}
		}
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		a = "ATCTGAT";
		b = "TGCATA";
		m = a.length();			
		n = b.length();
		char[] A = a.toCharArray();
		char[] B = b.toCharArray();
	
		LCSlength(A, B);			//求出dp
		Buildsubs(A);				//求出LCS
		
		System.out.println("求解结果:");
		System.out.println("	a:"+a);
		System.out.println("	b:"+b);
		System.out.print("	最长公共子序列:");
		for(int i=subs.size()-1; i>=0; i--) {
			System.out.print(subs.get(i)+" ");//(new StringBuffer(subs.toString())).reverse() 也可以将字符串逆置
		}
		//System.out.print((new StringBuffer(subs.toString())).reverse()+"\n");
		System.out.println("\n	长度:"+dp[m][n]);
	}

}

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值