最長公共子序列問題(LCS)
給定兩個序列
例如:
對於此問題,可以採用暴力求解的方式來比對,即窮舉出X的所有子序列,用每個子序列與y做一 一比較。假如X序列共有m個元素,對每個元素可以決定選或不選,則X的子序列個數共有
動態規劃
前幾篇博文已介紹了採用動態規劃的方法解決裝配線問題及鋼鐵切割問題,它們都滿足了兩個條件:1.具有最優子結構的特徵 2.子問題重疊。
最長公共子序列的特徵
首先定義前綴
則有以下定理:
假如序列
1.如果
2.如果
2.如果
上述定理即爲要求得X和Y的LCS,可以根據
由以上分析:令c[i][j]爲表示序列
計算LCS的長度
用數組c[m][n]保存子序列
從上到下,從做到右:複雜度爲
構造LCS
1.可以採用b[i][j]中記錄的值,依次倒序輸出,從b[7][6]開始倒序輸出。也可以用遞歸的方法,從底層開始,正序輸出;
2.不用b[i][j],即改進版,由於c[i][j]是由c[i-1][j-1]、c[i-1][j]、c[i][j-1],可以根據它們的關係,判斷出方向;運行時間都爲
例程
求
/************************************************************************
CSDN 勿在浮沙築高臺
http://blog.csdn.net/luoshixian099
算法導論--動態規劃(最長公共子序列)
2015年6月5日
main.cpp
************************************************************************/
#include<iostream>
using namespace std;
int b[8][7]={0},c[8][7]={0};
void Lcs_Length(char *X,char *Y) // 遍歷序列X和Y,自底向上計算出c[i][j]
{
for(int i=1;i<=7;i++)
for(int j=1;j<=6;j++)
{
if(X[i] == Y[j])
{
c[i][j] = c[i-1][j-1]+1;
b[i][j] = 2; //左上方
}
else if(c[i-1][j] >= c[i][j-1])
{
c[i][j] = c[i-1][j];
b[i][j]=1; //上方
}
else
{
c[i][j] = c[i][j-1];
b[i][j] = 3; //左方
}
}
}
void Print_Lcs(int b[][7],char *X,int i,int j) //採用b[i][j]遞歸輸出LCS
{
if(i == 0 || j == 0)
return ;
if(b[i][j] == 2)
{
Print_Lcs(b,X,i-1,j-1);
cout<<" "<<X[i];
}
else if(b[i][j] == 1)
Print_Lcs(b,X,i-1,j);
else
Print_Lcs(b,X,i,j-1);
}
void Print_Lcs(char *X,char *Y,int i,int j)//不用b[i][j],遞歸輸出LCS
{
if(i == 0 || j == 0)
return ;
if(X[i] == Y[j])
{
Print_Lcs(X,Y,i-1,j-1);
cout<<" "<<X[i];
}
else if(c[i-1][j] >= c[i][j-1])
Print_Lcs(X,Y,i-1,j);
else
Print_Lcs(X,Y,i,j-1);
}
int main()
{
char X[]={' ','A','B','C','B','D','A','B'};
char Y[]={' ','B','D','C','A','B','A'};
Lcs_Length(X,Y);
for(int i=0;i<8;i++) //輸出數組內的值
{
for(int j=0;j<7;j++)
cout<<" "<<c[i][j];
cout<<endl;
}
cout<<endl;
Print_Lcs(b,X,7,6); //輸出LCS
cout<<endl;
Print_Lcs(X,Y,7,6); //改進的版本
cout<<endl;
return 0;
}