最长公共子序列-动态规划

所谓一个字符串的子序列是指:从该序列中删除若干元素后所得到的序列;如hold为 helloworld的子序列。

 

考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bm-1”,并Z=“z0,z1,…,zk-1” 为它们的最长公共子序列。

不难证明有以下性质:

(1) 如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…,bn- 2”的一个最长公共子序列;

(2) 如果am-1!=bn-1,则若zk-1!=am-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…,bn-1” 的一个最长公共子序列;

(3) 如果am-1!=bn-1,则若zk-1!=bn-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…,bn-2” 的一个最长公共子序列。

这样,在找A和B的公共子序列时,如有am-1=bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”和“b0,b1,…,bm-2”的一个最长公共子序列;

如果am-1!=bn-1,则要解决两个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。

 

定义lcs[i][j]为序列“a0,a1,…,ai-1”和“b0,b1,…,bj-1”的最长公共子序列的长度,计算lcs[i][j]可递归地表述如下:

(1)lcs[i][j] = 0                                如果i=0或j=0;

(2)lcs[i][j] = lcs[i-1][j-1]+1                如果i,j>0,且a[i-1] = b[j-1];

(3)lcs[i][j] = max{lcs[i][j-1], lcs[i-1][j]} 如果i,j>0,且a[i-1] != b[j-1]。

按此算式可写出计算两个序列的最长公共子序列的长度函数。由于lcs[i][j]的产生仅依赖于lcs[i-1][j-1]、lcs[i-1][j]和lcs[i][j- 1],故可以从lcs[m][n]开始,跟踪lcs[i][j]的产生过程,逆向构造出最长公共子序列。细节见程序。

 1 package lcs;
 2 
 3 public class LCS {
 4     private static void ComSubStr(String str1,String str2){
 5         char[] a=str1.toCharArray();
 6         char[] b=str2.toCharArray();
 7         int aLen=a.length;
 8         int bLen=b.length;
 9         int[][] lcs=new int[aLen+1][bLen+1];
10         
11         for(int i=0;i<=aLen;i++)
12             for(int j=0;j<=bLen;j++)
13                 lcs[i][j]=0;
14         
15         for(int i=1;i<=aLen;i++)
16             for(int j=1;j<=bLen;j++)
17             {
18                 if(a[i-1]==b[j-1])
19                     lcs[i][j]=lcs[i-1][j-1]+1;
20                 else {
21                     lcs[i][j]=lcs[i][j-1]>lcs[i-1][j]?lcs[i][j-1]:lcs[i-1][j];
22                 }
23             }
24         
25         for(int i=0;i<=aLen;i++)
26         {
27             for(int j=0;j<=bLen;j++)
28             {
29                 System.out.print(lcs[i][j]+",");
30             }
31             System.out.println();
32         }
33         
34         int maxLen=lcs[aLen][bLen];
35         int i=aLen;
36         int j=bLen;
37         char[] comSubStr=new char[maxLen];
38         while(maxLen>0)
39         {
40             if(lcs[i][j]!=lcs[i-1][j-1])//说明a0......ai-1,b0.......bj-1中ai-1或bj-1为公共的;
41             {
42                 if(lcs[i][j-1]==lcs[i-1][j])//说明ai-1=bj-1为公共字符;
43                 {
44                     comSubStr[maxLen-1]=a[i-1];
45                     maxLen--;
46                     i--;
47                     j--;
48                 }
49                 else{//取两者中较长的作为a,b,的公共子序列
50                     if(lcs[i][j-1]>lcs[i-1][j])
51                         j--;
52                     else {
53                         i--;
54                     }
55                 }
56             }
57             else{
58                 i--;
59                 j--;
60             }
61         }
62         System.out.print("最长公共字符串是:");
63         System.out.println(comSubStr);
64     }
65     public static void main(String[] args){
66         String a = "blog.csdn.net";  
67         String b = "csdn.blogt";
68         LCS.ComSubStr(a, b);
69     }
70 }

 

输出结果为:

0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,1,1,1,1,1,
0,0,0,0,0,0,1,2,2,2,2,
0,0,0,0,0,0,1,2,3,3,3,
0,0,0,0,0,0,1,2,3,4,4,
0,0,0,0,0,1,1,2,3,4,4,
0,1,1,1,1,1,1,2,3,4,4,
0,1,2,2,2,2,2,2,3,4,4,
0,1,2,3,3,3,3,3,3,4,4,
0,1,2,3,4,4,4,4,4,4,4,
0,1,2,3,4,5,5,5,5,5,5,
0,1,2,3,4,5,5,5,5,5,5,
0,1,2,3,4,5,5,5,5,5,5,
0,1,2,3,4,5,5,5,5,5,6,
最长公共字符串是:csdn.t

  

 

posted @ 2015-09-09 16:50  enjoy_clh  阅读(149)  评论(0编辑  收藏  举报