參考文獻《演算法導論》
LCS問題:
描述,LCS英文:longest common string。中文:最長公用子序列。即找出兩個字串裡,最長的子序列。比如:
A D F G C 和 B F C 則最長的子序列為F C.
對應任意給點倆個字串,我們嘗試給出通用的演算法。
採用動態規劃,因為滿足動態規劃的特徵:
1.尋找的是最優問題
2.子問題互相間獨立
3.子問題能重疊,即子子最優是子最優的最優
遞推公式,引用書本上的,如下:
進行遞迴時,我們腦子裡時刻記住兩樣東西:
子問題是什麼
最優問題是什麼
子問題是程式遞迴的體現,最優問題是程式if的體現
看了半天,說實話,《演算法導論》上的方法 真沒看懂,結合代碼 終於開竅了。
嘗試自己分析一下,書上不是一堆公式,就是言簡意賅。
比如,給出兩個字串:“b d c a b a”和"a b c b d a b",採用動態規劃的思想,最優問題是:字串最長,用c[i,j]表示,其中 i,j是字串的位置標號 ;子問題是:c[i,j-1]和c[i-1,j]。引用書上的表格分析法
結合遞推公式,分析:i=1是,取A ,依次與j=1,2,3...比較,分兩種情況,當在i=1這行也取到A時,此時儲存格裡填入的數字,應該是對角線上的左上方的數字再加1,否則,比較儲存格上面和左面的數字,取大的放到儲存格裡。如:在座標(1,1)處,對應的是:A和B,不相等,觀察上邊和左邊的儲存格都是0,則填入0.在座標(1,4)處,A終於碰著了A,此時先查看座標(0,3)處的儲存格裡是0,則填入0+1=1.其餘類似,箭頭代表的就是取資料的來源,記下箭頭,可以在找到最長子序列時進行回溯哦。
上代碼:
void findLCS(string str1,string str2 ) { int m=str1.length(); int n=str2.length(); int c[50][50]; int mark[50][50]={0}; for(int i=0;i<m+1;++i) c[i][0]=0; for(int j=0;j<n+1;++j) c[0][j]=0; for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) { if(str1[i-1]==str2[j-1]) { c[i][j]=c[i-1][j-1]+1; mark[i][j]=1;//表示 } else if(c[i-1][j]>=c[i][j-1]) { c[i][j]=c[i-1][j];mark[i][j]=2;//表示↑ } else { c[i][j]=c[i][j-1]; mark[i][j]=3;//表示← } } for(int i=0;i<=m;++i) { for(int j=0;j<=n;++j) cout<<mark[i][j]; cout<<endl; } cout<<mark[0]<<" "<<mark[1]<<mark; printLCS(mark,m,n,str1); }
複雜度為:O(m*n)。
列印LCS:
void printLCS(int(* a)[50],int r,int c,string str){ if(r==0||c==0) return; if(a[r][c]==1) { printLCS(a,r-1,c-1,str); cout<<str[r-1]; } else if(a[r][c]==2) printLCS(a,r-1,c,str); else printLCS(a,r,c-1,str); }
複雜度:O(m+n)
測試:
int main(){string a="bdcaba";string b="abcbdab";findLCS(b,a); return 0;}
推薦一個解決矩陣相乘的動態規劃:http://blog.csdn.net/liguisen/article/details/2158127