用動態規劃法
求解問題特性:
a.具有重疊性
;b.具有最優子結構性質 1.
最長公共子串行
(lcs)
的概念: 若z
則稱z是x和y 的最長公共子串行,記為z
îlcs(x , y)。
最長公共子串行往往不止乙個。
e.g. x=, y=, 則
z=, z』=, z』』=
均屬於lcs(x , y) ,即x,y有3個lcs。
2.尋找最長公共子串行
由書上分析結果:
(1)若xm=y
n,則問題化歸成求x
m-1與y
n-1的lcs
(lcs(x , y)的長度等於lcs(x
m-1, y
n-1)的長度加1) (
2)若xm
≠yn則問題化歸成求x
m-1與y的lcs及x與y
n-1的lcs
lcs(x , y)的長度為:
max3.求取最長公共子串行
引進乙個二維陣列c,用c[i,j]記錄xi與y
j的lcs的長度
如果我們是自底向上進行遞推計算,那麼在計算c[i,j]之前,
c[i-1,j-1], c[i-1,j]與c[i,j-1]均已計算出來。此時我們
根據x[i]=y[j]還是x[i]
¹y[j],就可以計算出c[i,j]:
若x[i]=y[j],則執行c[i,j]←c[i-1,j-1]+1;若x[i]
¹y[j],則根據:
c[i-1,j]≥c[i,j-1],則c[i,j]取c[i-1,j];否則c[i,j]取c[i,j-1]。即有
c[i,j]=
e.g. 如下圖:
0 1
2 3
4 5 6
yj b d
c a
b a
0 x
i0
0 0
0 0
0 0 ←↑
↑←↑↖↖
1 a 0 ←
0 ←0←
0 1
←1 1
↖↑↖
2 b
0 1 ←1
←1 ←1
2 ←2
↑←↑↖←↑
←↑ 3 c
0 1 ←1
2 ←2←2
←2 ↖
←↑↑←↑↖
4 b
0 1
←1 2 ←
2 3 ←3
↑↖←↑
←↑↑←↑
5 d
0 1 2←
2 ←2 3 ←3
↑↑←↑
↖←↑↖
6 a
0 1 2←
2 3
←3 4
↖↑←↑↑
↖←↑7 b 0
1 2
←2
3 4 ←4
為了搜尋到所有的lcs的
,使用乙個m
´n的二維陣列b,
b[i,j]記錄c[i,j]是通過哪乙個子問題的值求得的,
以決定搜尋的方向:
若c[i-1,j]
>
c[i,j-1],則b[i,j]中記入「↑」; 若
c[i-1,j]=c[i,j-1]時,
則b[i,j]
中記入「←↑」,
若c[i-1,j]
執行的搜尋方向:
x[i]=y[j]要執行b[i,j]=「↖」,
x[i]
¹y[j]且c[i,j-1] > c[i-1,j]要執行b[i,j]=「←」,
x[i]
¹y[j]且c[i,j-1]
x[i]
¹y[j]且c[i,j-1] = c[i-1,j] 要執行b[i,j]=「←↑」,
演算法 lcs_l(x,y,m,n,c)
for i=0 to m do c[i,0]←0
for j=1 to n do c[0,j]←0
for i=1 to m do
}else if c[i-1,j]
>
c[i,j-1]
then
else if c[i-1,j]
=c[i,j-1]
then
}else
演算法的時間複雜度顯然是
q(m×n)的。
4 程式如下:
// 輸出lcs.cpp
#include
#include
#include
#include
#include
#include
using namespace std;
//*******************************尋找最長公共子串行***********************//
void lcs_l(int x,int y,int m,int n,int c[10],char d[10])
//i=0時取值為0
for(j=0;j<=n;j++)
//j=0時取值為0
for(i=1;i<=m;i++) //獲取c[i][j]的值
else
else if(c[i-1][j]==c[i][j-1])
else
} }
} cout<
for(k=0;k<=10;k++) //輸出二維陣列b
//呼叫顯示最長公共子串行函式
} //***********************輸出乙個最長公共子串行**********************//
void lcs_outputone(int c[10],int x,int y,int i,int j)
//到邊界是返回
if(x[i-1]==y[j-1]) //b[i][j]=='$'
else
if(c[i-1][j]>c[i][j-1])
lcs_outputone(c,x,y,i-1,j); // b[i][j]=='>'
else if(c[i-1][j]
lcs_outputone(c,x,y,i,j-1);
//b[i][j]='
else
lcs_outputone(c,x,y,i,j-1); //b[i][j]='=' }
//***********************輸出所有最長公共子串行**********************//
void lcs_outputall(int c[10],char d[10],int x,int i,int j,int result,int len,int k)
cout<
return; }
else
else
else if(d[i][j]=='
else
} }
} //***************************主函式********************************//
int main() ;
int b[9]=;
int c[11][10]=;
char d[11][10]=;
system("color fc");
srand(time(0));
cout<
for(i=0;i<10;i++)//
cout<
for(j=0;j<9;j++)//
lcs_l(a,b,10,9,c,d);//
cout<
for(i=0;i<11;i++) //顯示二維陣列c
len=c[i-1][j-1];//
x=10;
y=9;
k=len;
int result[len];
cout<
lcs_outputone(c,a,b,x,y);
cout<
cout<
lcs_outputall(c,d,a,x,y,result,len,k);
cout<
return 0; }
5.結果分析:
由lcs_output_all函式可知,求出所有的lcs
的長度就是根據陣列b[i][j]中的值,即搜尋的方向資訊來遍歷所有可能的路徑找到滿足條件的元素集合。函式lcs_l的時間複雜度是求解陣列b和c的執行時間,是o(mn+m+n)。而函式lcs_output_all的時間複雜度取決於遍歷的路徑數。在最好的情況下,即x序列和y序列完全相等,m=n,陣列b中的值都是『$
』(指向對角線方向),所以時間複雜度是o(m)。而在最壞情況下,即x序列和y序列不存在公共子串行,陣列b中的值都是「4」,就是要分別沿向上和向左的方向搜尋,這是每處理一次x[i]和y[j],都必須沿著兩個方向呼叫函式lcs_output_all,當遇到i=0或j=0的情況開始返回,只有在搜尋完所有的路徑後演算法才結束。要確定最壞情況的時間複雜度,就是要計算出從點(m,n)到i=0或j=0(除(i,j)=(0,0)外)的所有路徑。
LCS問題 動態規劃
簡述 lcs問題,即最長公共子串行問題,給定兩個序列x 和y 求x y最長的公共子串行。與lis類似,lcs也是可以不連續的。解題思路 本人覺得在這個問題上演算法導論講的很好,所以在此我主要是整理。1 首先我們來考慮暴力搜尋求解的方法,我們要暴力列舉x的所有子串行,然後再看看是不是也是y的子串行,這...
動態規劃 LCS計算
int findlcs string a,int n,string b,int m dp的第一行 for int j 0 j m 1 j 其他位置的dp值 for int i 1 i n 1 i else dp i j dp i 1 j dp i j 1 dp i 1 j dp i j 1 retu...
動態規劃解LCS問題
今天研究了一下動態規劃,思想很簡單 循序漸進,區域性推到整體。但運用起來還是不太好想的。下面用動態規劃解lcs最大公共子串問題 寫了兩個函式lcs和lcs inhance,前者用矩陣實現 用於理解原理 後者用兩個陣列實現 節省空間 如下 include includeusing namespace ...