算法起步之动态规划LCS

原文:算法起步之动态规划LCS

            前一篇文章我们了解了什么是动态规划问题,这里我们再来看动态规划另一个经典问题,最长公共子序列问题(LCS),什么是子序列,我们定义:一个给定序列将其中的0个或者多个元素去掉之后得到的序列就是他的子序列。例如序列x包含(a,b,c,b,d,a,b)那么序列y(b,c,d,b)就是x的一个子序列。公共子序列则是两个序列的公共的子序列,而最长公共子序列则是从两个序列的公共子序列中挑选出长度最长的子序列。

           我们用我们说的动态规划的四步来分析这个问题。一刻画最长公共子序列的特征。假设序列X(包含X1,X2……Xm)而序列Y(Y1……Yn)而他们的最长公共子序列为Z(Z1……Zk);那么1如果Xm=Yn则Zk-1是Xm-1与Yn-1的最大公共子序列。2如果Xm!=Yn且Zk!=Xm那么Zk是Xm-1与Yn的最大公共子序列。3如果Xm!=Yn且Zk!=Yn那么Zk是Xm与Yn-1的最大公共子序列。通过这3种情况我们就可以将我们刚才的问题刻画成一个递归的问题。

        我们可以通过递归的方式来求出最长公共子序列的长度。然后我们采用自底向上的方法来求出最优解。

public void lcsLength(String x,String y){
		int xl=x.length();
		int yl=y.length();
		int[][] map=new int[xl+1][yl+1];
		for (int i = 1; i <=xl; i++) {
			for (int j = 1; j <=yl; j++) {
				if (x.charAt(i-1)==y.charAt(j-1)) {
					map[i][j]=map[i-1][j-1]+1;
				}else {
					if(map[i-1][j]>map[i][j-1]){
						map[i][j]=map[i-1][j];
					}else {
						map[i][j]=map[i][j-1];
					}
				}

			}
		}
		System.out.println(map[xl][yl]);
	}

	public static void main(String[] args) {
		new LCS().lcsLength("abcab", "bcadd");
	}

        

           这个图是一个例子运行时数组中值得变化,大家看了这个图应该比较清楚。

           这样我们就求得了最长公共子序列的长度,然后我们可以根据改进算法把最长公共子序列打印出来。当然这里改进的方法有太多了,每个人可能都有自己的想法,我只提供一种方法供大家参考(并没有考虑运行效率等因素):

public class LCS {
	private StringBuffer sb=new StringBuffer();
	private int ox=0;
	private int oy=0;
	public void lcsLength(String x,String y){
		int xl=x.length();
		int yl=y.length();
		int[][] map=new int[xl+1][yl+1];
		for (int i = 1; i <=xl; i++) {
			for (int j = 1; j <=yl; j++) {
				if (x.charAt(i-1)==y.charAt(j-1)) {
					map[i][j]=map[i-1][j-1]+1;
						if (i>ox&&j>oy) {
							if(map[i][j]>sb.length()){
							sb.append(x.charAt(i-1));
							ox=i;oy=j;
							}
						}else {
							if (i>ox&&j<oy&&map[i][j]==sb.length()) {
								sb.deleteCharAt(sb.length()-1);
								sb.append(x.charAt(i-1));
								ox=i;oy=j;
							}
						}
				}else {
					if(map[i-1][j]>map[i][j-1]){
						map[i][j]=map[i-1][j];
					}else {
						map[i][j]=map[i][j-1];
					}
				}
			}
		}
		System.out.println(map[xl][yl]);
		System.out.println(sb.toString());
	}

	public static void main(String[] args) {
		new LCS().lcsLength("abcab", "bcadd");

	}

}

友情提示:转载请注明出处【作者:idlear  博客:http://blog.csdn.net/idlear

时间: 2024-08-31 03:45:43

算法起步之动态规划LCS的相关文章

算法起步之动态规划

原文:算法起步之动态规划        动态规划其实是类似于分治算法,说白就是要解决这类问题需要依赖一个个的子问题解决.动态规划通常是用来求解最优化问题,设计一个动态规划的算法一般需要四步: 刻画一个最优解的结构特征. 递归定义最优解的值. 计算最优解的值采用自底向上的方法. 利用计算出的信息构造一个最优解.       但是一般我们只需要前三步即可,第4步是根据最优值来求最优解的构成.       我们先通过一个具体的例子来了解一下.       下图是某公司出售的一段长度为i英寸的共条的价格

算法起步之Bellman-Ford算法

原文:算法起步之Bellman-Ford算法            从这篇开始我们开始介绍单源最短路径算法,他是图算法之一,我们前面说的贪心,图的遍历,动态规划都是他的基础,单源最短路径其实说的就是图中节点到节点的最短路径.就像我们使用百度地图从哪到哪一样,找出最近的距离,而单源最短路径问题不只是两点之间的路径,他有很多的变形,像单目的地最短路径问题,单节点对最短路径问题,所有节点对最短路径问题,最短路径的最优子结构问题.         在介绍这类算法之前我们先规定节点的基本属性,我们规定节点

算法起步之贪心算法

原文:算法起步之贪心算法        我们前面介绍的动态规划算法是求解最优化问题的一种通用方法,但是对于很多的最优化问题是用动态规划有点小题大做了,我们可以使用贪心算法,贪心算法相比动态规划更简单,也更高效.它总是做出局部最优选择,希望这样可以得到全局的最有选择.所以这种方法不能保证得到最优解,但是很多问题却都可以用这种方法.我们先看一个活动选择的例子.        假设我们有n个活动,只有一个教室,求在这个教室中一天最多可以举办多少活动(同一时间只能举办一个活动).下面给出的是活动的开始时

算法起步之拓扑排序

原文:算法起步之拓扑排序         拓扑排序是图中所有节点的一种线性次序,首先拓扑排序是对有向无环图来说的,如果不满足这个条件则无法拓扑排序,拓扑排序就是遵循一定的规则对节点的排序,规则就是假设在图中有一条边是从a节点出发指向b节点,则在拓扑排序中b节点不可能排在a的前面.其实就是一直寻找入度为0的节点.我们也可以理解成b的完成必须依赖a的完成,a没有完成则b无法进行,其实这样理解的话我们先前说的线性规划也是一种拓扑排序,像我们平时的起床过程也是一种拓扑排序.          拓扑排序的

算法起步之广度优先搜索

原文:算法起步之广度优先搜索           广度优先搜索算法是图的基本算法之一,图是用来保存过对多的关系的数据结构,相对于树一对多的关系更为复杂,所以难度也会比树结构难一点,图的存储一般有连接表表示跟链接矩阵表示,相比来说链接矩阵的方式更为常用,也就是用数组来存储.而广度优先搜索算法其实就是图的遍历过程,数组的遍历大家都会,数组的遍历我们是按照下标的顺序来遍历的,而图的遍历也有自己的方式,图是多对多的关系,我们可以按照各个节点直接的关联关系来遍历他们,这样便衍生出来深度优先跟广度优先,广度

算法起步之并查集(不相交集合数据结构)

原文:算法起步之并查集(不相交集合数据结构)          在java中经典的数据结构基本都给实现好了,我们可以直接调用,但是并查集这种数据结构却没有很好的替代工具,在这里我们我们自己去实现并查集数据结构.首先我们先要去了解什么是并查集.并查集也是一种非常经典常用的数据结构.像求连通子图跟我们下面要研究的最小生成树问题都会用到并查集.并查集就是能够实现若干个不相交集合,较快的合并和判断元素所在集合的操作.不相交集合:一个不相交集合维护了一个不相交动态集合{s1,s2,s3--}我们用一个代表

算法起步之快速排序

原文:算法起步之快速排序   快速排序一听这个名字可能感觉很快,但是他的算法时间复杂度最坏情况却跟插入排序是一样的.之所以成为快速排序是因为他的平均效率比堆排序还要快,快速排序也是基于分治思想与归并排序差不多,但是快速排序是原址的,直接在原数组操作不需要再开辟新的存储空间.快速排序的思想很简单,就是选定一个关键字k将原数组分成两份g1与g2,g1中所有的元素都比k小或者相等,而g2中所有的数据都比k大或者等于,这样对g1与g2分别进行快速排序,最终我们得到的就是一个有序的数组.代码中的sort方

算法起步之Prim算法

原文:算法起步之Prim算法            prim算法是另一种最小生成树算法.他的安全边选择策略跟kruskal略微不同,这点我们可以通过一张图先来了解一下.            prim算法的安全边是从与当前生成树相连接的边中选择一条最短的一条,并且该边是应是生成树与生成树外一点的连接.            所以我们prim算法用汉字描述的过程应为:1初始化2构造最小优先队列,将所有节点都加入到最小优先队列中,所有节点的key设置为无穷大,开始节点设置成0.3循环,直到队列为空{

算法起步之堆排序

原文:算法起步之堆排序          学习堆排序,首先需要明白堆的概念,堆是一个数组.可以近似当做完全二叉树的数组存储方式.但是跟他还有其他的性质,就是类似于二叉排序树.有最大堆跟最小堆之分,最大堆是指根节点的值都大于子节点的值,而最小堆的是根节点的值小于其子节点的值.堆排序一般用的是最大堆,而最小堆可以构造优先队列.堆里面有一个方法是用来维护堆的性质,也就是我们下面代码中的maxheap方法,这是维护最大堆性质的方法,第一个参数就是堆也就是数组,第二个参数是调整堆的具体节点位置,可能这个节