日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【谈谈】动态规划——求最长公共子序列

發布時間:2023/12/8 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【谈谈】动态规划——求最长公共子序列 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先,我們要搞清楚所謂最長公共子序列的概念。不然很容易把它和最長公共子串混淆,兩者區別是:子序列只需要字符保持相對順序,并不要求像公共字串那樣組成字符還需連續。
問題:
給定兩個字符串數組序列:

X[1...m] = {A, B, C, B, D, A, B} Y[1...n] = {B, D, C, A, B, A}

求它們的最長公共子序列,我們可肉眼判斷,這兩個字符串數組序列的最長公共子序列長度為4.

“BDAB” “BCAB” “BCBA”

現在用代碼解決這個問題,一般人比如說我最容易想到方法都是窮舉法:
列出X的所有子序列,再在Y中找到和其匹配的最長子序列。
如果用窮舉法解決,時間復雜度最多能達到O(n*2^m),也就是說,在一些特定場合比如校招面試,使用這種方法極易被pass。
再接來考慮遞推
問題的核心就是找到X,Y的最長公共子序列,記為LCS(X,Y)。

如果xm = yn,則Ck = xm = yn 且 Ck-1是Xm-1和Yn-1的一個LCS
如果xm != yn 且 Ck != xm,則C是Xm-1和Y的一個LCS
如果xm != yn 且 Ck != yn,則C是X和Yn-1的一個LCS

public class 遞歸實現 {public static int Lcs(char x[],char y[],int i,int j) {if(i==0||j==0) {return 0;}else if(x[i]==y[j]) {return Lcs(x,y,i-1,j-1)+1;}else {return Max(Lcs(x,y,i-1,j),Lcs(x,y,i,j-1));}}private static int Max(int a,int b){if(a>b) {return a;}else {return b;}} public static void main(String[] args) {//System.out.println((int)' ');String s1="ABCBDAB";char[] c1=new char[s1.length()+1];//帶0字符的字符數組char[] t1=s1.toCharArray();c1[0]=(char)0;for(int i=1;i<t1.length;i++) {c1[i+1]=t1[i];}String s2="BDCABA";char[] c2=new char[s2.length()+1];//帶0字符的字符數組char []t2=s2.toCharArray();c2[0]=(char)0;for(int i=1;i<t2.length;i++) {c2[i+1]=t2[i];}System.out.println(Lcs(c1,c2,c1.length-1,c2.length-1)) ; } }

但要是完全用遞歸求解,在整個二叉樹的最有求解過程中,會有大量的重復調用。時間復雜度是指數級,并沒有得到太大的優化。所以這里我們建議使用動態規劃求解。
為了杜絕相關不必要重復步驟,我們選擇用動態規劃求解,通過備忘錄或者說一張表來存放數據,避免重復的計算和調用,以空間換時間,同時本問題還符合動態規劃的基本特征,求解最優子結構和重復子問題。

備忘錄方法采用自頂向下方式,為每個解過的子問題建立了備忘錄以備需要時查看,避免了相同子問題的重復求解

public class 備忘錄法 {public static int Lcs(char x[],char y[],int i,int j,int bak[][]) {if(bak[i][j]!=-1) {return bak[i][j];}if(i==0||j==0) {return bak[i][j]=0;}else if(x[i]==y[j]) {return Lcs(x,y,i-1,j-1,bak)+1;}else {bak[i][j]=Max(Lcs(x,y,i-1,j,bak),Lcs(x,y,i,j-1,bak));}return bak[i][j];}private static int Max(int a,int b){if(a>b) {return a;}else {return b;}} public static void main(String[] args) {//System.out.println((int)' ');String s1="ABCBDAB";char[] c1=new char[s1.length()+1];//帶0字符的字符數組char[] t1=s1.toCharArray();c1[0]=(char)0;for(int i=1;i<t1.length;i++) {c1[i+1]=t1[i];}String s2="BDCABA";char[] c2=new char[s2.length()+1];//帶0字符的字符數組char []t2=s2.toCharArray();c2[0]=(char)0;for(int i=1;i<t2.length;i++) {c2[i+1]=t2[i];}//初始化備忘錄數組int [][]bak=new int[c1.length][c2.length];for(int i=0;i<c1.length;i++) {for(int j=0;j<c2.length;j++) {bak[i][j]=-1;}}System.out.println(Lcs(c1,c2,c1.length-1,c2.length-1,bak)) ; } }

備忘錄法相比于遞歸以空間換時間,降低了時間復雜度,但占用內存會大大升高,并且它的時間復雜度相比于下面這種方法并不算太過優化。
動態規劃—自底向上法:
采用自底向上方式,保存已求解的子問題,需要時取出,消除對某些子問題的重復求解.

import java.util.Scanner;public class 自底向上 {public static int Lcs(char x[],char y[],int i,int j,int bak[][]) {for(int ii=0;ii<=i;ii++) {for(int jj=0;jj<=j;jj++) {if(ii==0||jj==0) {bak[ii][jj]=0;}else if(x[ii]==y[jj]) {bak[ii][jj]= bak[ii-1][jj-1]+1;}else {bak[ii][jj]=Max(bak[ii-1][jj],bak[ii][jj-1]);}}}return bak[i][j];}private static int Max(int a,int b){if(a>b) {return a;}else {return b;}} public static void main(String[] args) {Scanner s=new Scanner(System.in);//System.out.println((int)' ');String s1=s.nextLine();char[] c1=new char[s1.length()+1];//帶0字符的字符數組char[] t1=s1.toCharArray();c1[0]=(char)0;for(int i=1;i<t1.length;i++) {c1[i+1]=t1[i];}String s2=s.nextLine();char[] c2=new char[s2.length()+1];//帶0字符的字符數組char []t2=s2.toCharArray();c2[0]=(char)0;for(int i=1;i<t2.length;i++) {c2[i+1]=t2[i];}int [][]bak=new int[c1.length][c2.length];System.out.println(Lcs(c1,c2,c1.length-1,c2.length-1,bak)) ; } }

總結

以上是生活随笔為你收集整理的【谈谈】动态规划——求最长公共子序列的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。