夜深人靜寫演算法——最長公共子序列(動態規劃)
阿新 • • 發佈:2018-12-20
一. 問題描述
給定兩個字串,求解這兩個字串的最長公共子序列(Longest Common Sequence)。比如字串1:BDCABA;字串2:ABCBDAB
則這兩個字串的最長公共子序列長度為4,最長公共子序列是:BCBA
二.尋找最優子結構,
用dp[i][j] 來表示第一個字串前 i 個字元和第二個字串前 j 個字元的最優解(最長公共子序列);
有兩種可能: (字元陣列 s1[n] 和字元陣列 s2[n] )
1 . s1[i] == s2[j] , dp[i][j] = dp[i-1][j- ]+1 : 如果s1[i] == s2[j] 那麼說明dp[i][j] 的最優解是dp[i-1][j-1]的最優解加一;
2 . s1[i] != s2[j] , 如果 dp[i][j-1] > dp[i-1][j] ,那麼dp[i][j] = dp[i][j-1] ; 如果dp[i-1][j] > d[i][j-1] ,那麼dp[i][j] = dp[i-1][j];
#include <iostream> #include <algorithm> #include <stdio.h> #include <stdlib.h> #include <math.h> #include <string.h> #define MAX 10 using namespace std; int MaxL(string &str1,string &str2,int BT[MAX][MAX]){ size_t size1 = str1.size(); //字串str1的長度 size_t size2 = str2.size(); //字串str2的長度 int **dp; dp = new int*[size1]; for(int i = 0; i < size1 ;i++) dp[i] = new int[size2]; for(int i = 0; i < size1;i++) dp[i][0] = 0; //如果str1的長度為0,最長公共子序列為0 for(int i = 0;i < size2;i++) dp[0][i] = 0; //如果str2的長度為0,最長公共子序列為0 for(int i = 1; i < size1;i++) for(int j = 1; j < size2;j++){ if(str1[j] == str2[i]){ //如果相等 dp[i][j] = dp[i-1][j-1] + 1; BT[i][j] = 1; //BT陣列用來記錄對於字串的操作,用來尋找最優子序列 }else if(dp[i-1][j] > dp[i][j-1]){ dp[i][j] = dp[i -1][j] ; BT[i][j] = 2; }else{ dp[i][j] = dp[i][j -1] ; BT[i][j] = 3; } } } void Traceback(int i,int j,int BT[MAX][MAX],string &x){ //通過記錄 if(i == 0 || j== 0) return; else{ if(BT[i][j] == 1){ //操作1:如果str1的前i個字元和str2的前j個字元的最優解進行的操作是 1 的話,就輸出str1[i],然後在追溯到str1的前i-1個字元和str2的前j-1個字元的最優解 Traceback(i-1,j-1,BT,x);cout<<x[i]; }else if(BT[i][j] == 2) Traceback(i-1,j,BT,x); //操作2:最優解進行的操作是2,不輸出,直接追溯到str1的i-1個字元和str2的j個字元的最優解 else Traceback(i,j-1,BT,x); //操作3:最優解進行的操作是2,不輸出,直接追溯到str1的i個字元和str2的j-1個字元的最優解 } } int main(){ string A = "ABCDEFGHTI"; string B = "BCDAEFHGIT"; int BT[10][10]; MaxL(A,B,BT); cout<<"最長公共子序列為:"; Traceback(10,10,BT,A); }