一、算法设计与分析:
设计LCS-LENGTH算法,概算福接受两个序列X[1..m]、Y[1...n]为输入。它将c[i,j]的值保存在表c[0…m,0…n],并按照行主序计算表项。过程维护一个表b[1…m,1…n],帮助构造最优解。B[i,j]指向的表项对应计算c[i,j]时所选择的子问题最优解。伪代码如下:
LCS-LENGTH(X,Y) m=X.length; n=Y.length; let b[1…m,1…n] and c[0…m,0…n] be new tables for i=1 to m c[i,0]=0; for j=0 to n c[0,j]=0; for i=1 to n for j=1 to n ifX[i]==Y[j] c[i,j]=c[i-1,j-1]+1; b[i,j]=’y’; else if c[i-1,j]>=c[i,j-1] c[i,j]=c[i-1,j]; b[i,j]=’u’; else c[i,j]=c[i,j-1]; b[i,j]=’l’; return c and b;
二、算法实现
#include<iostream> #include <iomanip> #define MAXX 1000 #define MAXY 1000 #include<stack> using namespace std; int LCS_LENGTH(char X[], char Y[], int **c, char **b); void PRINT_LCS(char **b, char X[], int i, int j);//递归 void PRINT_LCS2(char **b, char X[], int i, int j);//非递归 int main() { int len; int **c = new int*[MAXX+1]; for (int i = 0; i <= MAXX; i++) { c[i] = new int[MAXY+1]; } char **b = new char*[MAXX+1]; for (int i = 0; i <=MAXX; i++){ b[i] = new char[MAXY+1]; } char ch; //char X[] = {'0','A','B','C','B','D','A','B'}; //char Y[] = {'0','B','D','C','A','B','A'}; char *X = new char[MAXX + 1]; char *Y = new char[MAXX + 1]; X[0] = '0'; Y[0] = '0'; for (int i = 1; i <=MAXX; i++){ X[i] = 'A'+rand()%26; } for (int j = 1; j <= MAXY; j++) { Y[j] = 'A'+rand()%26; } len=LCS_LENGTH(X,Y,c,b); cout << "LCS长度为:"<< len << endl; cout << "一个LCS为:"; //PRINT_LCS(b, X, MAXX, MAXY);//递归输出,数据量大时递归栈溢出 PRINT_LCS2(b, X, MAXX, MAXY);//迭代输出 cout << endl; return 0; } int LCS_LENGTH(char X[], char Y[], int** c, char **b) { int i, j; for (i = 0; i <= MAXX; i++) { c[i][0] = 0; } for (j = 0; j <= MAXY; j++) { c[0][j] = 0; } for (i = 1; i <= MAXX; i++) { for (j = 1; j <= MAXY; j++) { if (X[i] == Y[j]) { c[i][j] = c[i - 1][j - 1] + 1; b[i][j] = 'y'; } else { if (c[i - 1][j] >= c[i][j - 1]) { c[i][j] = c[i-1][j]; b[i][j] = 'u'; } else { c[i][j] = c[i][j - 1]; b[i][j] = 'l'; } } } } return c[MAXX][MAXY]; } //递归输出 void PRINT_LCS(char **b, char X[],int i, int j) { if (i == 0 || j == 0) return; if (b[i][j] == 'y') { PRINT_LCS(b,X,i-1,j-1); cout << X[i]; } else { if (b[i][j] == 'u') { PRINT_LCS(b, X, i - 1, j); } else PRINT_LCS(b,X,i,j-1); } } //非递归输出 void PRINT_LCS2(char **b, char X[], int i, int j) { stack<char> lcs; while (!(i==0 || j==0)) { if (b[i][j] == 'y') { lcs.push(X[i]); //cout << X[i]; i--; j--; } else { if (b[i][j] == 'u') { i--; } else { j--; } } } while (!lcs.empty()) { cout << lcs.top(); lcs.pop(); } }
三、算法结果分析
首先采用算法导论书上例子进行测试:即X={A,B,C,B,D,A,B},Y={B,D,C,A,B,A}
程序运行结果如下:
可见运行结果与预期一致。
'A'+rand()%26随机生成两个10000个字母的序列,运行结果如下:
四、总结
该算法采用自底向上的动态规划算法,时间复杂度为O(mn),空间复杂度为也为O(mn),如果采用两个二维数组存储b和c,当序列较大时出现栈溢出的问题,所以采取在堆上分配二维数组空间的办法以处理较大的数据量。
当数据量比较大时递归的输出方法将会出现栈溢出问题,所以采取非递归的输出算法。