动态规划实现最长公共子序列

最长公共子序列的定义
  (1) 子序列

  给定两个序列X=<x 1 ,x 2 ,...,x n >和序列Z=<z 1 ,z 2 ,...,z k >,若存在X的一个严格递增下标序列<i 1 ,i 2 ,...,i k >,使得对所有j=1,2,...,k,有x ij =z j ,则称Z是X的子序列。如:Z=<B,C,D,B>是X=<A,B,C,B,D,A,B>的一个子序列,相应下标序列为<2,3,5,7>。
  (2)公共子序列
  对给定的两个序列X和Y,若序列Z既是X的的子序列,也是Y的子序列,则称Z是X和Y的 公共子序列 。如,X=<A,B,C,B,D,A,B>,Y=<B,D,C,A,B,A>,则序列<B,C,A>是X和Y的一个公共子序列。
  (3)最长公共子序列
  两个序列的长度最大的公共子序列称为它们的 最长公共子序列 。如,<B,C,A>是上面X和Y的一个公共子序列,但不是X和Y的最长公共子序列。最长公共子序列是<B,C,B,A>。怎么求最长公共子序列?
最长公共子序列问题( Longest-Common-Subsequence,LCS )——求(两个)序列的最长公共子序列
  (1)前缀:给定一个序列X=<x 1 ,x 2 ,...,x m >,对于i=0,1,...,m, 定义X的第i个前缀为X i =<x 1 ,x 2 ,...,x i >,即前i个元素构成的子序列。如,X=<A,B,C,B,D,A,B>,则X 4 =<A,B,C,B>,X 0 =Φ。

  (2)LCS问题的最优子结构性

  定理6.2 设有序列X=<x 1 ,x 2 ,...,x m >和Y=<y 1 ,y 2 ,...,y n >,并设序列Z=<z 1 ,z 2 ,...,z k >为X和Y的任意一个LCS。

  a.若x m =y n ,则z k =x m =y n ,且Z k-1 是X m-1 和Y n-1 的一个LCS。
  b.若x m ≠y n ,则z k ≠x m 蕴含Z是X m-1 和Y的一个LCS。
  c.若x m ≠y n ,则z k ≠y n 蕴含Z是X和Y n-1 的一个LCS。

  (3)递推关系式

  记,c[i,j]为前缀序列X i 和Y j 的一个LCS的长度。则有

  

  注: 以上情况涵盖了X m 和Y n 的LCS的所有情况。

  (4)给出一个例子

  上面黄色标注的并且为左上角标记的字符连起来就是最大公共子序列,即BCBA

 1 package cn.dp;
 2 
 3 /**
 4  * 最长公共子序列问题
 5  *
 6  */
 7 
 8 public class Test2 {
 9 
10     static int[][] result;
11     static String str1 = "ABCBDAB";
12     static String str2 = "BDCABA";
13     static char[][] b;
14     public static void main(String[] args) {
15             
16         result = new int[str1.length()+1][str2.length()+1];
17         b = new char[str1.length() + 1][str2.length() + 1];
18         LCS(str1, str2);
19         for (int i = 0; i < str1.length()+1; i++) {
20             for (int j = 0; j < str2.length()+1; j++) {
21                 System.out.print(result[i][j] + " ");
22             }
23             System.out.println();
24         }
25         System.out.println("最大公共子序列的长度为:" + result[str1.length()][str2.length()]);
26         System.out.println("最大公共子序列为:");
27         print_LCS(str1.length(), str2.length());
28     }
29     
30     /**
31      * 求出最大公共子序列的长度  并记录构造表信息
32      * @param str1
33      * @param str2
34      */
35     static void LCS(String str1, String str2) {
36         /*
37          * 初始化二位数组的边界
38          */
39         for (int i = 0; i <= str1.length(); i++) {
40             result[i][0] = 0;
41         }
42         for (int i = 0; i <= str2.length(); i++) {
43             result[0][i] = 0;
44         } 
45         for (int i = 1; i <= str1.length(); i++) {
46             for (int j = 1; j <= str2.length(); j++) {
47                 if(str1.charAt(i - 1) == str2.charAt(j - 1)) {
48                     result[i][j] = result[i - 1][j - 1] + 1;
49                     b[i][j] = '↖';
50                 } else if(result[i - 1][j] >= result[i][j-1]) {
51                     result[i][j] = result[i - 1][j];
52                     b[i][j] = '↑';
53                 } else {
54                     result[i][j] = result[i][j - 1];
55                     b[i][j] = '←';
56                 }
57             }
58         }
59     }
60     
61     /**
62      * 打印构造表信息
63      * @param i
64      * @param j
65      */
66     static void print_LCS(int i, int j) {
67         if(i == 0 || j == 0) {
68             return;
69         }
70         if(b[i][j] == '↖') {
71             print_LCS(i-1, j-1);
72             System.out.print(str1.charAt(i-1));
73         } else if(b[i][j] == '↑') {
74             print_LCS(i-1, j);
75         } else {
76             print_LCS(i, j-1);
77         }
78     }
79 }
动态规划实现最长公共子序列

 

 

posted @ 2018-11-30 21:53  夏末秋涼  阅读(509)  评论(0编辑  收藏  举报