动态规划解决最长子序列问题

来源:互联网 发布:windows怎么修复系统 编辑:程序博客网 时间:2024/05/18 02:09

  最长子序列是什么我就不过多的叙述了

  什么是动态规划我也不叙述了:动态规划就是把问题分解成一个个的小问题,并且小问题之间存在联系(可以用一些小问题的答案去求解另外小问题的答案)。

  最长子序列的求解思路:

  设第一个序列是X【0--m】第二个序列是Y【0--k】,现在我们要求这两个序列的最长公共子序列。

如果X[m] = Y[k]的话,那么X【0--m】与Y【0--k】的最长公共子序列就是X【0--m-1】与Y【0--k-1】最长公共子序列加上X【m】;

如果X[m]  !=  Y[k] 的话,那么X【0--m】与Y【0--k】的最长公共子序列就是 max{X【0--m-1】与Y【0--k】最长公共子序列,X【0--m】与Y【0--k-1】最长公共子序列};

  由上述的分析可知,如果要求的X【0--m-1】与Y【0--k】最长公共子序列,我们必须要知道前面的,前前面的短一点的序列的最长公共子序列,那么,这就是动态规划

用动态规划的思想把问题的解表示出来:

注意c[i,j]代表的是什么呢?代表的是序列X【0--i】与序列Y【0--j】的最长公共子序列的长度。


那么,我们如何用程序敲出来上述的解呢?答案是两个for循环即可,求出每一个i,j对应的长度,

那么,我们如何把最后的结果输出呢?答案是用递归的思想。

详情见代码

import java.util.Scanner;/* * 问题描述:两个字符串X,Y,输出这两个字符串的最长公共子序列 * 具体步骤: * 首先:我们要求出每一个C[i][j],利用两个for循环即可求出 * 然后:我们在求每一个C[i][j]的时候,记录该处的元素是否一样, *        用b[i][j]标记,b[i][j]为1时,代表两处元素相等 *        -1时,代表一种情况 *        0时,代表另外一种情况。 * 最后:输出公共子序列。 *       我们输出公共子序列的方法有,可以使用两次for循环,找出来b[i][j]为1的地方(方法1) *       方法1:不对的原因:eg:ssssss与序列sddddd, *       b[0][0],b[1][0],b[2][0]……b[5][0]都会是1,这样输出就会输出好多个S,最终结果错误。 *       或者: *       我们通过使用b[i][j]与c[i][j]之间的关系,来输出公共子序列。 * */public class Main {public static void main(String[] args){String X = new String();String Y = new String();Scanner sc = new Scanner(System.in);X = sc.nextLine();Y = sc.nextLine();String[] Xarr = X.split("");String[] Yarr = Y.split("");//注意被split函数分割之后,第一个元素为空。int[][] b = getLength(Xarr ,Yarr);Display(b,Xarr,Xarr.length-1,Yarr.length-1);System.out.println("ok");}public static int[][] getLength(String[] S1,String[] S2){    int[][] c = new int[S1.length][S2.length];//用来标记每一个C[i][j]的值    int[][] b = new int[S1.length][S2.length];//b用来存储哪一个位置的元素是相同的。    for(int i =1;i<S1.length;i++)    {    for(int j=1;j<S2.length;j++)    {    if(S1[i].equals(S2[j]))    {    //System.out.println("S[I] == S[J]");    c[i][j] = c[i-1][j-1] +1;    b[i][j] = 1;    }    else if(c[i][j-1] >= c[i-1][j])    {    c[i][j] = c[i][j-1];    b[i][j] = 0;    }    else    {    c[i][j] = c[i-1][j];    b[i][j] = -1;    }    }    }return b;}public static void Display(int[][] b,String[] s1,int i ,int j){/* * 为什么是||而不是&&呢? * 首先原因有:如果是&&的话,那么之后的操作Display(b,s1,i,j-1);之中的i-1,j-1就会变成负数 * 可是如果是||的话,就不会存在问题吗? * 存在:某一个序列的最后一个元素,要与另一个序列的其他元素(这个序列比较长)比较看是否相等? * 恩,我觉得应该和之前的getLength函数有关,两个for循环,已经把所有的c[i][j]的长度都求了出来。 * 而求出的这个c[i][j]正是最长的子序列的长度。再用b[i][j]记录ij处元素是否相等, * 如果元素相等的话,就把这两个相等的元素都去掉,Diaplay剩余的那些元素,这样就不会造成方法1的失误。 * 哦,那问题就解决了,一旦相等我们就会把这个元素去掉, * 所以当该序列值为0的话,就代表着所有的都已经去掉啦, * 所以就不会有我刚才的担心啦! * */if(i==0 || j==0){return ;}if(b[i][j] == 1){System.out.print(s1[i] +" ");Display(b,s1,i-1,j-1);}if(b[i][j] == 0){Display(b,s1,i,j-1);}if(b[i][j] == -1){Display(b,s1,i-1,j);}}}
其实,我的代码跟网上的普遍的代码使一样的,不过我在桥代码的时候,遇到了一些问题,并且把问题记录在了代码注释里面

问题1:为什么不能直接关注b[i][j]的值,如果b[i][j] == 1,就直接把X在i处的元素直接输出,原因住市里面已经写过啦

问题2: 在输出公共序列的时候,为什么return条件是“ || ” 而不是“&&”,原因我也在代码注释里面也写过啦




0 0
原创粉丝点击