动态规划
最长公共子序列问题
问题描述:
给定两个序列X={x1,x2,…,xn}和Y={y1,y2,…,yn},找出X和Y的一个最长公共子序列
说明:
- 1.子序列:
给定序列X={x1,x2,…,xn},Z={z1,z2,…,zn},若Z中的元素是X中的元素,当且仅当这些元素的按X原序列中的的顺序递增排列,则称Z是X的子序列- 2.公共子序列
给定序列X和序列Y,序列Z是X的子序列,也是Y的子序列,则称Z是X和Y的公共子序列- 3.最长公共子序列
包含元素最多的公共子序列即为最长公共子序列
最优子结构性质分析:
设Zk={z1,z2,…,zk}是序列Xm={x1,x2,…,xm}和序列Yn={y1,y2,…,yn}的最长公共子序列
1.若zk=xm=yn,则Zk-1={}是Xm-1和Yn-1的最长公共子序列
2.若xm≠yn,xm≠zk,则Zk是Xm-1和Yn的最长公共子序列
3.若xm≠yn,yn≠zk,则Zk是Xm和Yn-1的最长公共子序列
建立最优值得递归关系式:
设c[i][j]表示序列Xi和Yj的最长公共子序列的长度
算法设计:
- 步骤1:确定合适的数据结构
1.采用二维数组c来存放各个子问题的最优值,c[i][j]表示序列Xi和Yj的最长公共子序列的长度
2.二维数组b来存放各个子问题最优解的来源(如b[i][j]=1表示c[i][j]由c[i-1][j-1]+1得到;b[i][j]=2表示c[i][j]由c[i][j-1] 得到;b[i][j]=3表示c[i][j]由c[i-1][j] 得到)
数组x[] (长度为m)和y[] (长度为n)分别用来存放X序列和Y序列- 步骤2:初始化
令c[i][0]=0,c[0][j]=0 {0≤i≦m,0≦j≦n}- 步骤3:循环阶段,根据递归关系式,确定序列Xi和·Yj的最长公共子序列长度,1≦i≦m
步骤3-1:
i=1时,,求出c[1][j],同时记录c[1][j],1≦j≦n
步骤3-2:
1=2时,求出c[2][j],同时记录吧b[2][j],1≦j≦n
以此类推,直到
步骤3-m:
i=m时,求出c[m][j],同时记录b[m][j],1≦j≦n;此时c[m][n] 就是序列X和Y的最长公共子序列- 步骤4:
根据二维数组b记录的相关信息以自底向上的方式来构造最优解
步骤4-1:
初始时:i=m,j=n
步骤4-2:
如果b[i][j]=1,则输出x[i],同时递推到b[i-1][j-1];如果b[i][j]=2,则递推到b[i][j-1];如果b[i][j]=3,则递推到b[i-1][j];
重复执行步骤4-2,直到i=0或j=0,此时就可得到序列X和Y的最长公共子序列
c++代码:
//自底向上计算最优值,并记录相关信息
void LCSLength(){
int i,j;
for(i=1; i<=m; i++){
c[i][0] = 0;
}
for(i=1; i<=n;i++){
c[0][i] = 0;
}
for(i=1; i<=m; i++){
for(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][j] = c[i-1][j];
b[i][j] = 3;
}else{
c[i][j] = c[i][j-1];
b[i][j] = 2;
}
}
}
}
void LCS(int i,int j,char *x,int **b){
if(i == 0 || j == 0){
return;
}
if(b[i][j] == 1){
LCS(i-1,j-1,x,b);
cout<<x[i];
}else if(b[][] == 2){
LCS(i,j-1,x,b);
}else{
LCS(i-1,j,x,b);
}
}