zoukankan      html  css  js  c++  java
  • 一天一道算法题(4)---最长公共子序列

    • 题目

    给定两个字符串str1和str2,返回两个字符串的最长公共子序列

    • 解析

     本题是非常经典的动态规划问题,先来介绍求解动态规划表的过程。如果str1的长度为M,str2的长度为N,生成大小为M*N的矩阵dp,行数为M,列数为N。dp[i][j]的含义是str1[0..i]和str2[0..j]的最长公共子序列的长度。从左到右,再从上到下计算矩阵dp。

    1.矩阵dp第一列即dp[0..M-1][0],dp[i][0]的含义是str1[0..i]与str2[0]的最长公共子序列的长度。

    2.矩阵dp第一行即dp[0][0..N-1]与步骤一同理,如果str1[0]==str2[j],则令dp[0][j]=1

    3.对于其它位置(i,j),dp[i][j]的值只可能来自以下三种情况:

    • 可能是dp[i-1][j]
    • 可能是dp[i][j-1]
    • 可能是dp[i-1][j-1],就是str1[i]==str2[j]的时候,dp[i][j]=dp[i-1][j-1]+1

    这三个可能的值中,选最大的作为dp[i][j]的值,具体参看下面代码getdp方法。

      public int[][] getdp(char[] str1, char[] str2) {
            int[][] dp = new int[str1.length][str2.length];
            dp[0][0] = str1[0] == str2[0] ? 1 : 0;
            for (int i = 1; i < str1.length; i++) {
                dp[i][0] = Math.max(dp[i-1][0], str1[i] == str2[0] ? 1 : 0);  //最大也就是1了,因为只比较了str2的一个字符
            }
            for (int j = 1; j < str2.length; j++) {
                dp[0][j] = Math.max(dp[0][j-1], str1[0] == str2[j] ? 1 : 0); //同上
            }
            for (int i = 1; i < str1.length; i++) {
                for (int j = 1; j < str2.length; j++) {
                    dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
                    if (str1[i] == str2[j]) {
                        dp[i][j] = Math.max(dp[i][j], dp[i-1][j-1] + 1); //找到3中方式中最大的长度
                    }
                }
            }
            return dp;
        }

    dp矩阵中最右下角的值代表str1和str2的最长公共子序列的长度。通过整个dp矩阵可以得到最长公共子序列。具体如下:

    1.从矩阵的右下角开始,有三种移动方式:向上,向左,向左上。假设移动过程中,i表示此时的行数,j表示此时的列数,同时用一个变量res来表示最长公共子序列。

    2.如果dp[i][j]大于dp[i-1][j]和dp[i][j-1],说明在计算dp[i][j]的时候,一定是选择dp[i-1][j-1],可以确定str1[i]等于str2[j],并且这个字符一定属于最长公共子序列,把这个字符放进res,然后向左上方移动。

    3.如果dp[i][j]等于dp[i-1][j],向上方移动

    4.如果dp[i][j]等于dp[i][j-1],同上,向左方移动。

    5.如果dp[i][j]同时等于dp[i-1][j]和dp[i][j-1],向上还是向下无所谓

    通过dp求解最长公共子序列的过程:

      public String lcse(String str1, String str2) {
            if (str1 == null || str2 == null || str1.equals("") || str2.equals("")) {
                return "";
            }
            char[] chs1 = str1.toCharArray();
            char[] chs2 = str2.toCharArray();
            int[][] dp = getdp(chs1, chs2);
            int m = chs1.length - 1;
            int n = chs2.length - 1;
            char[] res = new char[dp[m][n]];
            int index = res.length - 1;
            while (index >= 0) {
                if (n > 0 && dp[m][n] == dp[m][n-1]) {
                    n--;
                } else if (m > 0 && dp[m][n] == dp[m-1][n]) {
                    m--;
                } else {
                    res[index--] = chs1[m];
                    m--;
                    n--;
                }
            }
            return String.valueOf(res);
        }
    • 参考资料

    《程序员代码面试指南》  左程云

  • 相关阅读:
    Erlang 督程 启动和结束子进程
    cocos2d-x 3.0 内存管理机制
    c语言基本数据类型
    4星|《剑桥中国经济史:古代到19世纪》:经济学视角看中国古代史
    孟晚舟三种结局;共享单车大败局;失业潮不会来:4星|《财经》2018年第30期
    2018左其盛差评榜,罕见的差书榜
    2018左其盛好书榜,没见过更好的榜单
    罗振宇时间的朋友2018跨年演讲中最重要的35句话
    中国土地制度与房价走势相关9本书
    2星|水木然《世界在变软》:肤浅的朋友圈鸡汤文
  • 原文地址:https://www.cnblogs.com/yangtong/p/6671169.html
Copyright © 2011-2022 走看看