1. 程式人生 > >LCS求最長公共子序列(DP)

LCS求最長公共子序列(DP)

遞推 劃分 get ima 維護 () arr har static

動態規劃並不是一種算法,而是一種解決問題的思路。典型的動態規劃問題,如最長公共子序列(LCS),最長單調子序列(LIS)等。

動態規劃分為四個步驟:

1.判斷問題是否具有最優子結構

這裏以LCS為例,X={x1,x2,...,xi};Y={y1,y2,...,yj}。最長公共子序列Z={z1,z2,...,zk};

①如果xi=yj,那麽zk=xi=yj,且Zk-1是序列Xi-1和Yj-1的LCS;

②如果xi≠yj,那麽zk≠xi;且Zk是序列Xi-1和Yj的LCS;

③如果xi≠yj,那麽zk≠yj;且Zk是序列Xi和Yj-1的LCS;

可以用反證法證明上述子結構的成立。

2.一個遞歸解

設c[i,j]是Xi和Yj的LCS的長度,i=0或j=0時,c[i,j]=0;

c[i,j]=0 (i=0或j=0)

c[i,j]=c[i-1,j-1] (i,j>0;xi=yj)

c[i,j]=max{c[i-1,j],c[i,j-1]} (i,j>0;xi≠yj)

3.計算LCS的長度

c[i,j]為遞歸解,那麽有多少個不同的遞歸解呢?O(m*n)。即要有重疊子問題,而不是每次都要解決新的問題。至於重疊子問題,需從底向上求。每次只需索引之前較小規模的子問題即可。

從底向上,求解c[i,j]。還需維護b[i][j]以簡化最優解的結構。

例如,設所給的兩個序列為X=<A,B,C,B,D,A,B>和Y=<B,D,C,A,B,A>。由算法LCS_LENGTH和LCS計算出的結果如圖2所示。

技術分享



package hello;

/*
* 本代碼通過動態規劃法獲取2個序列之間的最長公共子序列,並輸出序列的長度。
* 通過動態規劃法解決的問題往往都具有最優子結構性質和重疊子問題性質。
* 本題的最優子結構性質證明如下:
* 對於2個序列的最長公共子序列。如果x[m]=y[n],則最長公共子序列就是x[m-1]和y[n-1]的最長公共子序列+1。
* 如果x[m]和y[n]不相等,那麽最長公共子序列就是x[m]和y[n-1]或x[m-1]和y[n]之間最長公共子序列的最大值。
* 如果子序列的公共子序列不能達到最大,則該公共子序列一定不是最大的,與假設矛盾。
* 
* 遞歸方程為:c[i][j]=0 當 i=0,或j=0
* c[i][j]=c[i-1][j-1]+1 當i,j>0;x[i]=y[j];
* max{c[i][j-1],c[i-1][j]} i,j>0;x[i]不等於y[j]
* 
* 
* 
*/ public class getLongestString { public static void getLongest(char[] x,char[] y,int[][] longest,int[][] ch){ //將最長序列的邊界值進行初始化 for(int i=0;i<x.length;i++) longest[i][0] = 0; for(int i=0;i<y.length;i++) longest[0][i] = 0; //由底向上根據剛剛的遞推公式一層一層求解longest[i][j],通過ch[i][j]來記錄字母 for(int i=1;i<x.length;i++) for(int j=1;j<y.length;j++){ if(x[i] == y[j]) {longest[i][j] = longest[i-1][j-1]+1;ch[i][j]=1;} else if(longest[i-1][j]>longest[i][j-1]){ longest[i][j] = longest[i-1][j]; ch[i][j]=2; } else { longest[i][j] = longest[i][j-1]; ch[i][j] = 3; } } System.out.println("最長公共子序列長度為:"+longest[x.length-1][y.length-1]); System.out.print("該序列是:"); getString(x.length-1,y.length-1,x,y,ch); } //獲取最長的公共子序列,當出現第一種情況時,就可以提取出一個字母 public static void getString(int i,int j,char[] x,char[] y,int[][] ch){ if(i==0 || j==0) return; else if(ch[i][j]==1) { getString(i-1,j-1,x,y,ch); System.out.print(x[i]); } else if(ch[i][j]==2) getString(i-1,j,x,y,ch); else getString(i,j-1,x,y,ch); } public static void main(String[] args){ String str1 = " skowcgk"; String str2 = " dsofdwvk"; char[] ch1 = str1.toCharArray(); char[] ch2 = str2.toCharArray(); int[][] longest = new int[ch1.length][ch2.length]; int[][] ch = new int[ch1.length][ch2.length]; getLongest(ch1,ch2,longest,ch); } }

LCS求最長公共子序列(DP)