动态规划法求解最长公共子序列(含Java代码)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
公共子序列问题徐康123183
一.算法设计
假设有两个序列X和Y,假设X和Y分别有m和n个元素,则建立一个二维数组C[(m+1)*(n+1)],记录X i与Y j的LCS的长度。
将C[i,j]分为三种情况:
若i =0 或j =0时,C[i,j]=0;
若i,j>0且X[i]=Y[j],C[i,j]=C[i-1,j-1]+1;
若i,j>0且X[i] Y[j],C[i,j]=max{C[i-1,j],C[i,j-1]}。
再使用一个m*n的二维数组b,b[i,j]记录C[i,j]的来向:
若X[i]=Y[j],则B[i,j]中记入“↖”,记此时b[i,j] = 1;
若X[i] Y[j]且C[i-1,j] > C[i,j-1],则b[i,j]中记入“↑”,记此时B[i,j] = 2;
若X[i] Y[j]且C[i-1,j] < C[i,j-1],则b[i,j]中记入“←”,记此时B[i,j] = 3;
若X[i]Y[j]且C[i-1,j] = C[i,j-1],则b[i,j]中记入“↑”或“←”,记此时B[i,j] = 4;
得到了两个数组C[]和B[],设计递归输出LCS(X,Y)的算法:
LCS_Output(Direction[][], X[], i, j, len,LCS[]){
If i=0 or j=0 将LCS[]保存至集合LCS_SET中
then return;
If b[i,j]=1 then /*X[i]=Y[j]*/
{LCS_Output(b,X,i-1,j-1);
将X[i]保存至LCS[len-i];}
else if b[i,j]=2 then /*X[i]Y[j]且C[i-1,j]>C[i,j-1]*/
LCS_Output(b,X,i-1,j)
else if b[i,j]=3 then /*X[i]Y[j]且C[i-1,j]<C[i,j-1]*/ LCS_Output(b,X,i,j-1)
else if b[i,j]=4 then /*X[i]Y[j]且C[i-1,j]=C[i,j-1]*/
LCS_Output(b,X,i-1,j)
LCS_Output(b,X,i,j-1)
}
二.算法时间复杂度分析
由上述对算法的分析得知,求辅助数组C 和B 所消耗的时间复杂度为O (mn ),而查找所有的公共子序列的时间复杂度取决于所遍历的路径,而路径是由算法递归的方向决定的。
显然,最好的情况是m=n 并且B 中的所有值都为1(按斜对角线方向搜索),此时时间复杂度为O (n )。
当X 和Y 序列不存在公共子序列时为算法的最坏情况,因为此时C 数组的所有元素都为0,B 在每一个节点都要沿着两个不同的方向搜索,即每次都要调用两次LCS_Output,当调用到i=0或j=0时返回,直到搜索完整个m*n 数组才结束。
该时间复杂度就是计算从点(m,n )到i=0或j=0的所有路径。
建立如上图的直角坐标系,设点S (m ,n ),x 轴上的坐标点P 1(1,0) 到Pm(m,0),y 轴上的系列坐标点Q 1(0,1) 到Qn(0,n)。
因为j i Q P 和是搜索路径的边界上的点,点1+i P 不能直接到达点i P ,点1+j Q 也不能直接到达j Q ,所以点),(n m S 到m i P P P P ,...,,...,,21和n j Q Q Q Q ,...,,...,,21的路径数等价于),(n m S 到点
)1,('),...,1,('),...,1,2('),1,1('21m P i P P P m i 和点),1('),...,,1('),...,2,1('),1,1('21n Q j Q Q Q n j 的路径数,又因为
点),(n m 到),(j i 路径数为i
n j i n m C ---+,设总路径数为t ,则有
()()
m m
n n m n n m n n m n m m n n m n n n m m n m m n m m n m n n m n m
j j
m j
m n n
i i
n i
n m C C C C C C C C C C C C C C C C
t ++-+--+--+--+---+--+---+--+=--+-=--+-===+=+=+++++++++=+=∑∑11111110
11231201123121
11
1...... 三.程序流程图如下所示
四.程序源码
package Homework2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
public class LCS {
int C[][];
int B[][];
Set LCS_SET = new HashSet(); //使用集合去重
public int LCSLength(char X[], char Y[]) { //返回公共子序列长度int m = X.length;
int n = Y.length;
C = new int[X.length][Y.length];
B = new int[X.length][Y.length];
for (int i = 0; i < m; i++) {
C[i][0] = 0;
B[i][0] = 0;
}
for (int j = 0; j < n; j++) {
C[0][j] = 0;
B[0][j] = 0;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (X[i] == Y[j]) {
C[i][j] = C[i - 1][j - 1] + 1;
B[i][j] = 1;
} else if (C[i - 1][j] > C[i][j - 1]) {
C[i][j] = C[i - 1][j];
B[i][j] = 2;
} else if (C[i - 1][j] < C[i][j - 1]) {
C[i][j] = C[i][j - 1];
B[i][j] = 3;
} else {
C[i][j] = C[i - 1][j];
B[i][j] = 4;
}
}
}
return C[m - 1][n - 1];
}
public void LCS_Output(int Direction[][], char X[], int i, int j, int len, char LCS[]) {
int lcslen = len;
if (i == 0 || j == 0) {
LCS_SET.add(String.valueOf(LCS));
return;
}
if (B[i][j] == 1) {
LCS[len - 1] = X[i];
len--;
LCS_Output(B, X, i - 1, j - 1, len, LCS);
} else if (B[i][j] == 2) {
LCS_Output(B, X, i - 1, j, len, LCS);
} else if (B[i][j] == 3) {
LCS_Output(B, X, i, j - 1, len, LCS);
} else if (B[i][j] == 4) {
LCS_Output(B, X, i - 1, j, len, LCS);
LCS_Output(B, X, i, j - 1, len, LCS);
}
}
/**
*@param args
*/
public static void main(String[] args) throws IOException { // TODO Auto-generated method stub
char X[], Y[];
BufferedReader buf = null;
buf = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入一个序列");
X = buf.readLine().toCharArray();
char X_temp[] = new char[X.length + 1];
for (int i = 0; i < X.length; i++) {
X_temp[0] = ' ';
X_temp[i + 1] = X[i];
}
System.out.println("请输入一个序列");
Y = buf.readLine().toCharArray();
char Y_temp[] = new char[Y.length + 1];
for (int i = 0; i < Y.length; i++) {
Y_temp[0] = ' ';
Y_temp[i + 1] = Y[i];
}
System.out.print("X=");
for (char x : X) {
System.out.print(x);
}
System.out.println();
System.out.print("Y=");
for (char y : Y) {
System.out.print(y);
}
System.out.println();
LCS lcs = new LCS();
int len = lcs.LCSLength(X_temp, Y_temp);
char LCS[] = new char[len];
int m = X.length;
int n = Y.length;
System.out.println("最长公共子序列长度为:" + len); // 输出最长子序列长度System.out.print("最长公共子序列有:");
lcs.LCS_Output(lcs.B, X_temp, m, n, len, LCS);
System.out.print(lcs.LCS_SET); // 输出子序列集合中的元素}
}
五.运行结果截图
1.输入abc和acb
2.输入asd和xcv
3.输入ABCBDAB 和BDCABA
经检查输出结果均正确!。