所谓一个字符串的子序列是指:从该序列中删除若干元素后所得到的序列;如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