博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

华为OJ之最长公共子序列

Posted on 2017-04-24 13:54  Yonguos  阅读(377)  评论(0编辑  收藏  举报

题目描述:

对于两个给定的字符串,给出他们的最长公共子序列。

题目分析:

1,在之前的博文(http://www.cnblogs.com/yonguo123/p/6711360.html)中我们讨论了最长公共子串的问题,当时也指出最长公共子串其实就是最长公共子序列的一种特殊情况,那么今天我们就来一起研究一下最长公共子序列的求解;

2,最长公共子序列的定义这里不再赘述,和之前的最长公共子串一样,我们这次仍然采用动态规划和回溯的思想来解决;

3,我们假设输入为str1, str2, 首先来通过动态规划获取最大最序列的长度:

   3-1,构建一个二维数组tempMatrix,维度为(str1.length + 1), (str2.length + 1);

     3-2,temp[i][j]的含义:str1.subString(0, i)和str2.subString(0, j)的最长公共子序列(注意理解这个数组的含义)的长度;

     3-3,显然temp[i][j] == 0, 当i == 0 || j == 0;

     3-4,否则,当 str1.charAt(i - 1) == str2.charAt(j - 1)时,temp[i][j] = temp[i - 1][j - 1] + 1, 否则temp[i][j] == max{temp[i, j - 1], temp[i - 1, j]}(具体原因大家自己思考一下,可以从新加入的两个尾字符是否会出现在新的LCS中来思考);

4,已经构建好了tempMatix矩阵,实际上tempMatrix[str1.length][str2.length]就是LCS的长度了,而且tempMatrix也保存了必要的回溯信息,下面我们继续构建一个String[][] flagMatrix = new String[str1.length][str2.length], 规则如下:

  

  可能会有同学对这个左上,上,左的取值有疑问,请看下面这张回溯图:

  

 

  实际上回溯时具体的方向与我们对于坐标的选取是有关系的,所以大家在写代码之前,应该先把坐标方向规定好。之后的工作就是利用flagMatrix来逐步回溯,详细的实现细节见代码。

5,如果还有其它更好的想法,欢迎大家与我交流^-^

具体代码(Java实现):

 1 import java.util.Scanner;
 2 
 3 public class LCSequence {
 4     public static void main(String[] args) {
 5         String[] paras = getInput();
 6         System.out.println(getLongestSubsequence(paras[0], paras[1]));
 7     }
 8     
 9     public static String[] getInput() {
10         Scanner reader = new Scanner(System.in);
11         String[] paras = new String[2];
12         paras[0] = reader.nextLine();
13         paras[1] = reader.nextLine();
14         reader.close();
15         return paras;
16     }
17     
18     public static String[][] getGenerationMatirx(String str1, String str2) {
19         int[][] temp = new int[str1.length() + 1][str2.length() + 1];
20         String[][] flag = new String[str1.length() + 1][str2.length() + 1];
21         for (int i = 0; i <= str1.length(); i ++) {
22             temp[i][0] = 0;
23         }
24         for (int i = 0; i <= str2.length(); i ++) {
25             temp[0][i] = 0;
26         }
27         //迭代获取最长公共子序列的生成矩阵
28         for (int i = 1; i <= str1.length(); i ++) {
29             for (int j = 1; j <= str2.length(); j ++) {
30                 if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
31                     temp[i][j] = temp[i - 1][j - 1] + 1;
32                     flag[i][j] = "left_up";
33                 } else {
34                     if (temp[i - 1][j] >= temp[i][j - 1]) {
35                         temp[i][j] = temp[i - 1][j];
36                         flag[i][j] = "up";
37                     } else {
38                         temp[i][j] = temp[i][j - 1];
39                         flag[i][j] = "left";
40                     }
41                 }
42             }
43         }
44         return flag;
45     }
46     public static String getLongestSubsequence(String str1, String str2) {
47         String[][] flagMatrix = getGenerationMatirx(str1, str2);
48         String tempString = "";
49         for (int i = str1.length(), j = str2.length(); i > 0 && j > 0;) {
50                 if (flagMatrix[i][j].equals("left_up")) {
51                     tempString += str1.charAt(i - 1);
52                     i --;
53                     j --;
54                 } else if (flagMatrix[i][j].equals("left")) {
55                     j --;
56                 } else {
57                     i --;
58                 }
59         }
60         return new StringBuffer(tempString).reverse().toString();
61     }
62 
63 }

 

运行结果:

    

  这里需要注意一点,因为LCS可能不止一个,所以对于flagMatrix的赋值其实不止一种情况,代码34行中大于或者大于等于都可以,但是可能会输出不同的结果。