问题描述
对于母串X={x1,x2,⋯,xm}, Y={y1,y2,⋯,yn}, 求LCS与最长公共子串。
假设 Zk={z1,z2,⋯,zk} 是 X 与 Y 的 LCS, 可以观察到
- 如果xm=yn,则 zk=xm=yn,有 Zk−1 是 Xm−1、Yn-1 的 LCS;
- 如果xm≠yn ,则 Zk 是 Xm 与 Yn−1 的LCS,或者 Xm−1 与 Yn 的 LCS
利用动态规划思想,不难写出状态转移方程:
1 2
| if X[i]=Y[j] then LCS(i,j) = LCS(i-1j-1)+1 else LCS(i,j) = max(LCS(i-1,j) , LCS(i,j-1))
|
也就很容易有代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static int lcs(String X, String Y) { int lx = X.length(); int ly = Y.length(); int[][] r = new int[lx+1][ly+1]; for (int i = 0; i <= lx; i++) { for( int j = 0; j <= ly; j++) { if(i == 0 || j == 0) { r[i][j] = 0; } else if (X.charAt(i-1) == Y.charAt(j-1)) { r[i][j] = r[i-1][j-1] + 1; } else { r[i][j] = max(r[i - 1][j], r[i][j - 1]); } } } return c[lx][ly]; }
|
实际上这样带来的空间消耗还是比较大,观察到每次实际只用到了上一行保存的数字(不考虑复原LCS,只考虑计算其长度),可以将m*n的数组转为2*n的数组。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public static int lcs(String X, String Y) { int lx = X.length(); int ly = Y.length(); int[][] r = new int[2][ly+1]; r[0][0]=0; int k =0; for (int i = 0; i <= lx; i++) { k = 1-k; for( int j = 1; j <= ly; j++) { if(i == 0) { r[i][j] = 0; } else if (X.charAt(i-1) == Y.charAt(j-1)) { r[k][j] = r[1-k][j-1] + 1; } else { r[k][j] = Math.max(r[1-k][j], r[k][j - 1]); } } } return r[k][ly]; }
|
求最长公共子串与上面类似,转移方程为:
1 2
| if X[i]=Y[j] then LCS(i,j) = LCS(i-1j-1)+1 else LCS(i,j) = 0
|