算法导论——最长公共子序列

一、算法设计与分析:

设计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,当序列较大时出现栈溢出的问题,所以采取在堆上分配二维数组空间的办法以处理较大的数据量。

当数据量比较大时递归的输出方法将会出现栈溢出问题,所以采取非递归的输出算法。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值