算法设计与分析:第四章 动态规划 4.5最长公共子序列

/*
最长公共子序列:
 假定,A=a 1 a 2 …a n 是字母表∑上的一个字符序列,如果存在
∑上的另外一个字符序列S=c 1 c 2 …c j ,使得对所有的k,
k=1,2,…,j,有c k =a ik (其中,1≤ik≤n),是字符序列A的一个
下标递增序列,则称字符序列S是A的子序列。

如果∑={x,y,x}, ∑上的字符序列是A=xyzyxzxz,则xxx是A的
一个长度为3的子序列。该子序列中的字符,对应于A的下
标是1,5,7。而xzyzx是A的长度为5的子序列,对应于A的下
标是1,3,4,6,7.

分析:
令序列A=a1a2...an和B=b1b2...bm,记A=a1a2...ak为A中最前面连续k个字符的子序列,
序列A和序列B的最长公共子序列性质:
1)若an = bm,序列Sk = c1c2...ck是A和B的最长公共子序列,有an = bm = ck,
  且序列Sk-1=c1c2...ck-1是序列An-1和Bm-1的长度为k-1的最长公共子序列

2)若an != bm,且an != ck,那么应该将序列A最后字符an排除,用An-1与B进行比较
Sk 是An-1与Bm的长度为k的最长公共子序列

3)若an != bm,且bm != ck,Sk是An与Bm-1的长度为k最长公共子序列

划分状态:
第一阶段:计算A1和Bj的LCS长度L1,j  ,j = 1,2,...,m
第二阶段: 计算A2和Bj的LCS长度L2,j  ,j = 1,2,...,m
...
第n阶段 :计算An和Bj的LCS长度Ln,j  ,j = 1,2,...,m
第n阶段中的Ln,m是序列An和Bm的最长公共子序列的长度


搜索状态:
设置二维状态数组si,j。
si,j = 1,ai = bj
si,j = 2,ai != bj,且Li-1,j >= Li,j-1,序列A优先
si,j = 3,ai != bj,且Li-1,j < Li,j-1,序列B优先

设Ln,m=k,Sk=c1c2...ck是An和Bm的长度为k的最长公共子序列,搜索过程从状态字Sn,m开始。
若sn,m=1,an = bm -> 搜索sn-1,m-1
若sn,m=2,an !=bm,且Ln-1,m >= Ln,m-1,序列A优先 -> 搜索sn-1,m
若sn,m=3,                                             sn,m-1
搜索起点:i = n,j = m
搜索终止:i = 0 或 j = 0 

先通过初始状态,从上向下,从左向右填表,填表利用递推
然后把表填完,从最后再开始搜索
递推和动态规划的结合


思想:
1)记Ln,m为序列An和Bm的最长公共子序列的长度,则为Li,j为序列Ai和Bj的长度。

2)状态迁移方程:Ln,m = {Ln-1,m-1 + 1,an = bm,n > 0 ,j >0
					  {max{Ln-1,m  ,Ln,m-1},an!= bm
3)边界:	Li,0 = L0,j = L0,0 = 0,1<=i<=n,1<=j<=m
  
4)目标状态:Ln,m

算法过程:
1填表:
L[i][j] = {L[i-1][j-1] + 1,ai = bj,1<=i<=n,1<=j<=m,
		  {max(L[i-1][j] , L[i][j-1]), ai != bj,1<=i<=n,1<=j<=m
		  {0,i = 0 或者 j = 0
2搜索:
S[n][m] = {1,an = bm
		  {2,an != bm && L[i-1][j] >= L[i][j-1]
		  {3,an != bm && L[i-1][j] < L[i][j-1]
3 n = 0 或 m = 0算法结束




输入:
xyxzyxyzzy xzyzxyzxyzxy
xyzyxzxz xzyxxyzx
xzyxxyzx xyzyxzxz(先后顺序不同,答案不同)
输出:
8
xyxzxyzy
6
xzyxzx
6
xzyxxz
*/

#include <iostream>
#include <string>
#include <string.h>
#include <vector>

using namespace std;
const int MAXSIZE = 100;


int max(int a,int b)
{
	return a > b ? a : b;
}

int LCS(string& sA,string& sB,vector<char>& vecLCS)
{
	//鲁棒性
	if(sA.empty() || sB.empty())
	{
		return 0;
	}
	int n = sA.size();
	int m = sB.size();

	//设定递推数组的初始值
	int iLCSArr[MAXSIZE][MAXSIZE];
	//memset(iLCSArr,0,sizeof(iLCSArr));

	//自顶向下递推
	for(int i = 0 ; i < n ; i++)
	{
		for(int j = 0 ; j < m ; j++)
		{
			if(i != 0 && j != 0)
			{
				if(sA.at(i) != sB.at(j))
				{
					iLCSArr[i][j] = max(iLCSArr[i-1][j],iLCSArr[i][j-1]);
				}
				else
				{
					iLCSArr[i][j] = iLCSArr[i-1][j-1] + 1;
				}
			}
			else
			{
				iLCSArr[i][j] = 0;
			}
		}
	}
	//测试,打印长度表
	cout << "长度表:" << endl;
	for(int i = 0 ; i < n ; i++)
	{
		for(int j = 0 ; j < m ;j++)
		{
			cout << iLCSArr[i][j];
		}
		cout << endl;
	}

	//填表
	int iMatrix[MAXSIZE][MAXSIZE];
	for(int i = 0 ; i < n ; i++)
	{
		for(int j = 0 ; j < m ; j++)
		{
			if(sA[i] == sB[j])
			{
				iMatrix[i][j] = 1;
			}
			else
			{
				//序列A优先
				if(iLCSArr[i-1][j] >= iLCSArr[i][j-1])
				{
					iMatrix[i][j] = 2;
				}
				//序列B优先
				else
				{
					iMatrix[i][j] = 3;
				}
			}
		}
	}

	//测试,打印填表
	cout << "\n状态表:" << endl;
	for(int i = 0 ; i < n ; i++)
	{
		for(int j = 0 ; j < m ;j++)
		{
			cout << iMatrix[i][j];
		}
		cout << endl;
	}
	cout << endl;

	//搜索
	int iRet = 0;
	for(int i = n -1 ; i >= 0 ;)
	{
		for(int j = m - 1 ; j >= 0 ; )
		{
			//相等
			if(iMatrix[i][j] == 1)
			{
				iRet++;
				vecLCS.push_back(sA[i]);
				i--;
				j--;
			}
			//序列A优先,直接向上走,i--
			else if(iMatrix[i][j] == 2)
			{
				i--;
			}
			//序列B优先,直接向左走,j--
			else if(iMatrix[i][j] == 3)
			{
				j--;
			}
		}
	}
	return iRet;
}



void process()
{
	string sA,sB;
	vector<char> vecLCS;
	cout << "请输入两个字符串:" << endl;
	while(cin >> sA >> sB)
	{
		vecLCS.clear();
		printf("%d\n",LCS(sA,sB,vecLCS));
		for(int i = vecLCS.size() - 1; i>= 0 ;i-- )
		{
			cout << vecLCS.at(i); 
		}
		cout << endl;
	}
}

int main(int argc,char* argv[])
{
	process();
	getchar();
	return 0;
}

你可能感兴趣的:(动态规划,算法设计与分析)