最長公共子序列(LCS問題)的DP解法
阿新 • • 發佈:2019-01-31
呃。。大一做過,畢竟是ACM入門DP題,但是大三的我已然忘了具體咋做了,只記得是DP,面試常會問這個問題,所以有必要搞明白。
題目描述略。
解題思想就是DP,DP無外乎需要知道兩個東西,一是狀態是什麼,二是狀態之間的遞推關係是什麼。
這道題是一個二維DP,使用狀態dp[i][j]表示str1取到第i個字元(包括i),str2取到第j個字元(包括j)時,最長公共子序列的長度。(i,j取值從1開始)
遞推關係為:
dp[i][j] = 0 (i = 0或j = 0)
dp[i][j] = max(
dp[i-1][j-1] + 1, (若str1[i] = str2[j])
dp[i-1][j],
dp[i][j-1]
);
綜上,就可以寫出一個求最長公共子序列長度的程式碼了,可以用HDU1159驗證,該題是LCS模板題。
//注意下面程式碼裡實際儲存的字串是從0開始,dp陣列是從1開始。
#include <cstdio> #include <iostream> #include <string> #include <cstring> using namespace std; int dp[1000][1000]; int main(){ string str1,str2; while(cin>>str1>>str2){ memset(dp, 0, sizeof(dp)); int m = str1.length(); int n = str2.length(); for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){ if(str1[i-1] == str2[j-1]){ dp[i][j] = dp[i-1][j-1] + 1; } dp[i][j] = max(dp[i-1][j], dp[i][j]); dp[i][j] = max(dp[i][j-1], dp[i][j]); } } cout<<dp[m][n]<<endl; } return 0; }
然後筆試面試時候,往往還會讓你輸出這個最長公共子序列,我使用的方法是建立一個結構體,每個DP狀態上都保留了該狀態是從哪個狀態遞推來的,以及該狀態上是否增添了新的相等字元。這樣就可以從dp[m][n]狀態一直回溯到該狀態最開始是從什麼狀態推出來的了,也就知道了最長公共子序列是什麼。
程式碼如下,該程式碼裡註釋部分可以除錯輸出,方便理解:
#include <cstdio> #include <iostream> #include <string> #include <stack> #include <cstring> using namespace std; struct NODE{ int len; int preX; int preY; bool selected; NODE(){ len = 0; preX = -1; preY = -1; selected = false; } NODE(int l, int x, int y, bool s){ len = l; preX = x; preY = y; selected = s; } }; NODE dp[1000][1000]; string str1,str2; int m,n; void printLCS(){ cout<<"最長公共子序列長度為:"<<dp[m][n].len<<endl; cout<<"該序列為:"; stack<char> st; int i = m, j = n; while(i!=-1 && j!=-1){ //cout<<i<<" "<<j<<" get "<<dp[i][j].preX<<" "<<dp[i][j].preY<<endl; if(dp[i][j].selected){ st.push(str1[i-1]); } int preI = dp[i][j].preX; int preJ = dp[i][j].preY; i = preI, j = preJ; } if(st.empty()){ cout<<"無"<<endl; } while(!st.empty()){ cout<<st.top(); st.pop(); } cout<<endl<<"---------------------------------"<<endl; } int main(){ while(cin>>str1>>str2){ m = str1.length(); n = str2.length(); for(int i=0;i<=m;i++){ for(int j=0;j<=n;j++){ dp[i][j] = NODE(); } } for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){ if(str1[i-1] == str2[j-1]){ dp[i][j] = NODE(dp[i-1][j-1].len + 1, i-1, j-1, true); //cout<<i<<" "<<j<<" is from "<<i-1<<" "<<j-1<<" val is "<<dp[i][j].len<<" s is true"<<endl; } if(dp[i-1][j].len>dp[i][j].len){ dp[i][j] = NODE(dp[i-1][j].len, i-1, j, false); //cout<<i<<" "<<j<<" is from "<<i-1<<" "<<j<<" val is "<<dp[i][j].len<<" s is false"<<endl; } if(dp[i][j-1].len>dp[i][j].len){ dp[i][j] = NODE(dp[i][j-1].len, i, j-1, false); //cout<<i<<" "<<j<<" is from "<<i<<" "<<j-1<<" val is "<<dp[i][j].len<<" s is false"<<endl; } } } /*for(int i=0;i<=m;i++){ for(int j=0;j<=n;j++){ cout<<dp[i][j].len<<" "; } cout<<endl; }*/ printLCS(); } return 0; }