最长公共子序列

Description
给定两个字符串,返回两个字符串的最长公共子序列(不是最长公共子字符串),可能是多个。
Input
输入第一行为用例个数, 每个测试用例输入为两行,一行一个字符串
Output
如果没有公共子序列,不输出,如果有多个则分为多行,按字典序排序。
Sample Input 1 
1 1A2BD3G4H56JK 23EFG4I5J6K7
Sample Output 1
23G456K 23G45JK
 
解题思路:
先看一下对于两个字符串str1和str2,能不能利用已有的信息推出进一步的信息。
这里用n表示str1长度,m表示str2长度,有i∈[0,n-1] , j∈[0,m-1], dp[i][j]表示str1从0-i 与 str2从o-j的最长公共子序列长度。
要计算dp[i][j],可以这样分情况讨论:
  如果str1[i]=str2[j], 那么肯定最大公共子序列包含这个字符,则dp[i][j] = dp[i-][j-1]+1;
  如果str1[i]!=str2[j],那么继续判断:
    比较dp[i-1][j]与dp[i][j-1],哪个大就选择哪一个,一样大,说明都可以选择。dp[i][j] = Math.max(dp[i][j-1],dp[i-1][j])
然后可以将递归的过程用递推实现。
实现代码如下:
 1 int[][] dp = new int[str1.length()+1][str2.length()+1];
 2 int n = str1.length()+1;
 3 int m = str2.length()+1;
 4 // 计算dp数组
 5 for (int i = 1; i < n; i++) {
 6     for (int j = 1; j < m; j++) {
 7         if (str1.charAt(i-1) == str2.charAt(j-1))
 8             dp[i][j] = dp[i-1][j-1]+1;
 9         else
10             dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
11     }
12 }
计算出长度后,要求出公共子序列,可以根据dp[][]反推回去。
令i=n-1, j=m-1。
如果str1[i]=str2[j],那么当前点一定包含在最长子序列中,接下来直接进入dp[i-1][j-1];
如果str1[i]!=str2[j],继续判断:
比较dp[i-1][j]与dp[i][j-1],哪个大就选择哪一个,进入Math.max(dp[i][j-1],dp[i-1][j]),但是不记录字符,因为不是两边字符串相同字符。
这里比较特殊的是dp[i][j-1]=dp[i-1][j]的情况,这里都进入的话可能会有重复字符串,所以最后需要去重。
 
完整代码如下:
 1 import java.util.*;
 2 
 3 public class Main { 
 4 
 5     public static void main(String[] args) {
 6         Scanner scan = new Scanner(System.in);
 7         int x = scan.nextInt();
 8         scan.nextLine();
 9         // n个测试样例
10         for (int k = 0; k < x; k++){
11             String str1 = scan.nextLine();
12             String str2 = scan.nextLine();
13             int[][] dp = new int[str1.length()+1][str2.length()+1];
14             int n = str1.length()+1;
15             int m = str2.length()+1;
16             // 计算dp数组
17             for (int i = 1; i < n; i++) {
18                 for (int j = 1; j < m; j++) {
19                     if (str1.charAt(i-1) == str2.charAt(j-1))
20                         dp[i][j] = dp[i-1][j-1]+1;
21                     else
22                         dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
23                 }
24             }
25             // 逆向推导公共子序列,因为需要计算所有可能的结果,所以这里用dfs实现
26             List<String> lists = new ArrayList<>();
27             StringBuilder sb = new StringBuilder();
28             dfs(lists, sb, dp, str1, str2, n-1, m-1);
29             // 输出lists
30             if (!lists.isEmpty()) {
31                 Collections.sort(lists, String::compareTo);
32                 for (int i = 0; i < lists.size(); i++) {
33                     if (i == 0 || !lists.get(i).equals(lists.get(i-1)))
34                         System.out.println(lists.get(i));
35                 }
36 
37             }
38         }
39 
40     }
41     // dfs推导公共子序列
42     public static void dfs(List<String> lists, StringBuilder sb, int[][] dp, String str1, String str2, int i, int j) {
43         // 如果超出范围或者dp[i][j]=0,则退出递归,退出前将list插入lists
44         if (dp[i][j] == 0 || i == 0 || j == 0) {
45             if (sb.length() != 0) {
46                 StringBuilder temp = new StringBuilder(sb);
47                 temp.reverse();
48                 lists.add(temp.toString());
49             }
50             return;
51         }
52 
53         // 如果str1.charAt(i-1) == str2.charAt(j-1),则直接记录并进入dp[i-1][j-1]
54         if (str1.charAt(i-1) == str2.charAt(j-1)) {
55             sb.append(str1.charAt(i-1));
56             dfs(lists, sb, dp, str1, str2, i-1, j-1);
57             // 因为有分支,所以这里回溯下sb
58             sb.deleteCharAt(sb.length()-1);
59         } else {
60             // 如果str1.charAt(i-1) != str2.charAt(j-1),则比较dp[i-1][j]和dp[i][j-1],进入更大的那个
61             if (dp[i-1][j] == dp[i][j-1]) {
62                 dfs(lists, sb, dp, str1, str2, i-1, j);
63                 dfs(lists, sb, dp, str1, str2, i, j-1);
64             } else if (dp[i-1][j] > dp[i][j-1]) {
65                 dfs(lists, sb, dp, str1, str2, i-1, j);
66             } else {
67                 dfs(lists, sb, dp, str1, str2, i, j-1);
68             }
69         }
70 
71     }
72 }

 

posted @ 2021-03-21 22:46  凝冰物语  阅读(45)  评论(0编辑  收藏  举报