【經典動態規劃問題】最長公共子序列LCS
目錄
從題目出發分析如何用動態規劃求解最長公共子序列問題
題目
給定兩個字串A和B,返回兩個字串的最長公共子序列的長度。例如,A="1A2C3D4B56”,B="B1D23CA45B6A”,”123456"或者"12C4B6"都是最長公共子序列。
給定兩個字串A和B,同時給定兩個串的長度n和m,請返回最長公共子序列的長度。保證兩串長度均小於等於300。
測試樣例:
"1A2C3D4B56",10,"B1D23CA45B6A",12
返回:6
題目分析
狀態
dp[i][j],表示A[0,...i]和B[0,...j]的最長公共子序列長度。
邊界值討論
當i = 0時,dp[i][j]求的是A[0]和B[0,...j]的最長公共子序列長度,遍歷B陣列,如B[k] == A[0],也就是在B中找到了等於A[0]的值,最長公共子序列就是A[0],則dp[0][k,k+1,...m] = 1;
同理,當j = 0時,dp[i][j]求的是A[0,...i]和B[0]的最長公共子序列長度,遍歷A陣列,如A[t] == B[0],最長公共子序列就是B[0],則dp[t,t+1,...n][0] = 1;
其他情況討論
求一般情況下的dp[i][j],只可能出現兩種情況:
1.如果A[i] == B[j],dp[i][j] = dp[i-1][j-1]+1; 如‘ABCD’和‘ABDD’的最長公共子序列長度等於‘ABC’和‘ABD’的最長公共子序列長度+1,因為末尾元素D一定會包含在'ABCD'和'ABDD'的最長公共子序列中。
2.如果A[i]!=B[j],那麼,A[0,...i]和B[0,...j]的最長公共子序列要麼從A[0,..i]和B[0,...j-1]中取得,要麼從A[0,..i-1]和B[0,...j]中取得;比較兩個值中的最大值,即為dp[i][j]的值;如‘ABCD’和‘ABD4’中最長公共子序列的長度等於‘ABC’和‘ABD4'的最長公共子序列長度,要麼等於‘ABCD’和‘ABD’的最長公共子序列長度,我們只需要取最大值即可。
程式碼實現
class LCS { public: int findLCS(string A, int n, string B, int m) { // write code here //dp[i][j]表示A[0,...i]和B[0,...j]的最長公共子序列的長度 int dp[301][301] = {0}; for(int i = 0;i<n;i++){//第一列中,找出A[i] = B[0],dp[i][0] = 1,且dp[i+1,...n][0] = 1; if(A[i]==B[0]) // { for(int j = i;j<n;j++){ dp[j][0] = 1; } } } for(int i = 0;i<m;i++){//第一行中,找出A[0] = B[i],dp[0][i] = 1,且dp[0][i+1,...m] = 1; if(A[0]==B[i]) // { for(int j = i;j<m;j++){ dp[0][j] = 1; } } } //接下來考慮一般情況,dp[i][j]可能來自哪些情況: // A[i] != B[j] ,A[0...i]和B [0,...j-1]的最長公共子序列為dp[i][j-1] //如果A[i] == B[j],dp[i][j] = dp[i-1][j-1]+1 // A[i] != B[j] ,A[0...i-1]和B [0,...j]的最長公共子序列為dp[i-1][j] int max = 0; for(int i = 1;i<n;i++){ for(int j = 1;j<m;j++){ if(A[i] == B[j]) max= dp[i-1][j-1]+1; else{ max = (dp[i][j-1] > dp[i-1][j] ? dp[i][j-1]: dp[i-1][j]); } dp[i][j] = max; } } return dp[n-1][m-1]; } };