题目描述:
有两个字符串,求这两个字符串的最长的公共子序列的长度(一般情况下,默认是求公共子序列的长度)。
在刷题的过程中页有遇到这样的要求:求公共子序列的长度,并输出最长公共子序列。
问题说明:
如下的代码中,虽然成功的输出了公共子序列,但是总感觉有些不足之处。因为在自己编写的两个字符串中,存在长度相同的最长公共子序列。
带来的问题就是,我们输出的子序列不具有唯一性。在不同的编程题目中,有可能会和最终的结果出现差异。可以调整一下方法中的参数,使用不
同的字符串作为方法的参数,得到的结果也是不同的。
特点分析:
关于两个字符串的最长公共子序列,特点是子序列的字符在原来的字符串中的顺序是一致的,但是并不要求所有的字符都是连续的。
数据结构:
假设Z是str1和str2的公共子序列
str1的长度我们用x来表示
str2的长度我们用y来表示
建立状态转换矩阵为dp[i][j],用来表示两个字符串的最长公共子序列的长度。
通过分析题目我们可以得到下面的三个结论:
当str1下标为i的字符刚好等于str2下标为j的字符dp[i][j] = dp[i -1][j -1] + 1
当str1的第i个字符和str2的第j个字符不相同的时候,那么dp[i][j] 的值就是dp[i -1][j]和dp[i ][j -1]中最大的那个
通过记录每次得到公共字符的某种情况,可以反推得到这个字符。
示例代码如下:
public class LCSS { public static final int THIS = 1; public static final int UP = 2; public static final int LIFT = 3; public static void main(String[] args) { String str1 = "abcdcfe"; String str2 = "abcfedc"; int len1 = str1.length(); int len2 = str2.length(); int[][] dp = new int[len1+1][len2+1]; int[][] status = new int[len1+1][len2+1];// 保存确定最大公共子序列时的三个状态,方便后续查找出对应的字符 int res = getLCS(str1,str2,dp,status); System.out.println(res); print_LCS(len1,len2,str1,status); } private static void print_LCS(int len1,int len2,String str1, int[][] status) { if (len1 == 0 || len2 == 0){ return; } if (status[len1][len2] ==THIS){ print_LCS(len1-1,len2-1,str1,status); System.out.print(str1.charAt(len1-1)); }else if(status[len1][len2] ==UP){ print_LCS(len1,len2-1,str1,status); }else{ print_LCS(len1-1,len2,str1,status); } } private static int getLCS(String str1, String str2, int[][] dp, int[][] status) { for (int i =0;i<=str1.length(); i++){ // 在状态转换矩阵中,第一行的值是已知的.都是0 dp[i][0] = 0; status[i][0] = 0; } for (int j=0;j<=str2.length(); j++){ // 在状态转换矩阵中,第一列的值是已知的.都是0 dp[0][j] = 0; dp[0][j] = 0; } for (int i = 1; i<=str1.length(); i++){ for (int j =1;j<=str2.length();j++){ if(str1.charAt(i-1) == str2.charAt(j-1)){ dp[i][j] = dp[i-1][j-1]+1; status[i][j] = THIS; }else{ if(dp[i-1][j]<=dp[i][j-1]){ dp[i][j] = dp[i][j-1]; status[i][j] = UP; }else{ dp[i][j] = dp[i-1][j]; status[i][j] = LIFT; } } } } return dp[str1.length()][str2.length()]; } }