zoukankan      html  css  js  c++  java
  • 最长公共子序列的长度,和构造

            最长公共子序列即是两个字符串都包含的一个字符序列,但是不需要连续。例如:

                          String s1 : abfc

                          String s2: abcd

    "abc"既是s1的一个子序列,也是s2的一个子序列,因此"abc"是他们的最长公共子序列。

    简单总结下思路: 使用动态规划,用一个二维数组dp[i][j]表示 s1的1--i与s2的1--j的子串的最长公共子序列。例如:dp[2][3] 表示s1的 ”ab“ 和s2的 ”abc“的最长公共子序列。由子问题的结果逐步往上得到最终解。动态规划的最关键就是递推的定义最优解,最长公共子序列的递推公式为:

    dp[i][j] = dp[i-1][j-1] + 1        (ch1[i] == ch2[j])

    dp[i][j] = Max(dp[i-1][j] , dp[i][j-1])     (ch1[i] != ch2[j])

     (ch1[] ch2[] 分别为s1 s2转化来的字符数组)

      dp[i][j] = 0                    (i==0 || j == 0)

    代码如下:

    import java.util.*;
    public class LCS {
        public static void main (String [] args) {
            Scanner sc = new Scanner(System.in);
            while(sc.hasNext()) {       //输入两个字符串
                String s1 = sc.next();
                String s2 = sc.next();
                System.out.println("
    "+LCSLen(s1,s2));
            }
        }
    
        //最长公共子序列的长度
        static int LCSLen(String s1,String s2) {
            char [] ch1 = s1.toCharArray();
            char [] ch2 = s2.toCharArray();
            int[][] flag = new int[ch1.length+1][ch2.length+1];      //flag[i][j]记录dp[i][j]的值是如何得来的
            int [][] dp = new int[ch1.length+1][ch2.length+1];      //dp[i][j]表示ch1的0--i子串 与 ch2的0--j子串 的最长公共子序列长度
            for(int i = 1; i <= ch1.length; i++) {
                for(int j = 1; j <= ch2.length; j++) {
                    if(ch1[i-1] == ch2[j-1]) {
                        dp[i][j] = dp[i-1][j-1] + 1;           //ch1的i位置与ch2的j位置二者字符相同,则长度可以+1
                        flag[i][j] = 1;
                    }
                    else if(dp[i-1][j] >= dp[i][j-1]) {      //否则取dp[i-1][j] dp[i][j-1]二者中较大值,二者相等这里取前者。
                        dp[i][j] = dp[i-1][j];
                        flag[i][j] = 2;
                    }
                    else if(dp[i][j-1] > dp[i-1][j]) {
                        dp[i][j] = dp[i][j-1];
                        flag[i][j] = 3;
                    }
                }
            }
            getLCS(s1,s1.length(),s2.length(),flag);
            return  dp[ch1.length][ch2.length];
        }
        
        //构造一个最长公共子序列
        static void getLCS(String s1,int n,int m,int[][] f) {
            StringBuffer sb = new StringBuffer();
            char[] ch = s1.toCharArray();
            while(n > 0 && m > 0) {
                if(f[n][m] == 1) {          //根据flag[][]记录的标志找出一个最长公共子序列
                    sb.append(ch[n-1]);
                    n--;
                    m--;
                }
                if(f[n][m] == 2) {      //dp[i-1][j] >=dp[i][j-1],所以s1的当前字符应为更左边一个,n--
                    n--;
                }
                if(f[n][m] == 3) {
                    m--;
                }
            }
            System.out.println(sb.reverse());   //上述字符都是从右至左挑选的,所以要反转一下输出
        }
    }
  • 相关阅读:
    ORACLE 使用笔记
    Python资源大全,让你相见恨晚的Python库
    基于python的k-s值计算
    sklearn聚类模型:基于密度的DBSCAN;基于混合高斯模型的GMM
    skearn学习路径
    透彻形象理解核函数
    LDA降维与PCA降维对比
    sklearn 岭回归
    GBDT、XGBOOST、LightGBM对比学习及调参
    sklearn,交叉验证中的分层抽样
  • 原文地址:https://www.cnblogs.com/shen-qian/p/11316320.html
Copyright © 2011-2022 走看看