1. 程式人生 > >動態規劃解決最長公共子序列問題

動態規劃解決最長公共子序列問題

一、相關概念

1 子序列:如果序列Z={z1,z2,z3…zk},可以由序列X={x1,x2,x3…xn}刪除(或不刪除)一些元素得到,則Z為X的一個子序列。Z維持了X序列的相對順序。 2 動態規劃:使用分治法將問題劃分成一個個子問題,當分解的問題共享子問題時,可採用動態規劃。由於分治法在解子問題時存在重複計算,而動態規劃只計算一次每個子問題並將其解儲存到一個表中,從而避免重複計算。

二、問題描述

求序列Xn={x1,x2,x3…xn} 和序列Ym={y1,y2,y3…ym}的最長公共子序列

三、解決方案

1、思路:

設最長公共子序列為Z={z1,z2,…zk} 如果xn=ym,則xn(ym)一定在Z中,則繼續在{x1,x2,…xn-1}(記為Xn-1,後面表示均採用這種記法)和Ym-1中繼續尋找最長公共子序列; 若 xn!=ym,則尋找max(Xn-1和Ym的最長公共子序列,Xn與Ym-1的最長公共子序列)

2、虛擬碼

(1) 採用遞迴實現 lcs(X[],Y[]) { n<-X.length m<-Y.length if(X[n-1]=Y[m-1]) return lcs(Xn-1,Ym-1)+1 else return max(lcs(Xn-1,Y),lcs(Xn,Ym-1)) print(X[n-1]) } 由於遞迴在運算的過程中存在很多重複子問題,所以一般使用迭代實現動態規劃 (2)迭代實現 藉助輔助陣列B[n][m] , C[n][m] (C[i][j]表示Xi與Yj的lcs的長度)

在這裡插入圖片描述 在這裡插入圖片描述 求最終解時,從B[n][m] 逆序遍歷到下標i或j有一個是0,打印出B[i][j]="b"的C[i][j] 這樣就求得X和Y的最長子序列的逆序,然後做些調整,或者使用遞迴直接打印出正序輸出的最長子序列也可以。 關鍵過程的虛擬碼如下: lcs(X[],Y[]) { n<-X.length m<-Y.length for(i=0;i<n;i++) C[i][0]=0 //Y字串長度為0的情況 for(i=0;i<m;i++) C[0][j] =0 //X字串長度為0的情況 for(i=1;i<n;i++) { for(j=1;j<m;j++) { if(X[i]=Y[j]) { C[i][j]=C[i-1][j-1]+1 B[i][j]=“b” } else if(C[i-1][j-1]>C[i][j-1]) { C[i][j]=C[i-1][j] //由Xi-1和Yj確定 B[i][j]=“c” } else { C[i][j]=C[i][j-1] //由Xi和Yj-1確定 B[i][j]=“a” } } } return B&C } 上面虛擬碼得到了B和C,接著只要逆序遍歷B,輸出值為“b”對應的下標i,j的C[i][j],即可,不過這樣輸出的序列是最長子序列的逆序,可以將其逆序。 使用迭代實現可以看出時間複雜度為O(mn),空間複雜度為O(mn)