题目描述:
对于两个给定的字符串,给出他们的最长公共子序列。
题目分析:
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行中大于或者大于等于都可以,但是可能会输出不同的结果。