首页 > 代码库 > 一天一道算法题(4)---最长公共子序列
一天一道算法题(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); }
这只是打印出一种子序列,如果把所有的子序列都打印出来怎么做,明天更新。
-
参考资料
《程序员代码面试指南》 左程云
一天一道算法题(4)---最长公共子序列