1. 程式人生 > >演算法實踐--最長公共子序列(Longest Common Subsquence)

演算法實踐--最長公共子序列(Longest Common Subsquence)

什麼是最長公共子序列

X=ACCG

Y=CCAGCA

長度為1的公共子序列: {A} {C} {G}

長度為2的公共子序列:{AC} {CC} {CG} {AG}

長度為3的公共子序列:{ACG}

長度為4的公共子序列

 

最長公共子序列即為 {ACG}

 

問題:長度為N和M的兩個序列如何求他們的最長公共子序列?

X = ACCGGGTTACCGTTTAAAACCCGGGTAACCT

 

Y = CCAGGACCAGGGACCGTTTACCAGCCTTAAACCA

 

簡單演算法

for (int i=N; i>0; --i) {

  找到X所有長度為i的子序列;

  找到Y所有長度為i的子序列;

  如果存在公共子序列則終止

}

 

複雜度O(2^N)

 

動態規劃演算法

定義C[i][j]為序列X[i..i]和Y[1..j]的最長子序列的長度

C[i][0]和C[0][j]==0

 

遞推公式

C[i][j]=C[i-1][j-1]+1,  X[i]==Y[j]

C[i][j]=MAX(C[i-1][j], C[i][j-1]),  X[i]!=Y[j]

 

C++實現

template<typename T>
void prtM(vector< vector<T> >& arr, string msg = "") {
    cout << msg << " " << endl;
    for  (auto i: arr) {
        for (auto j: i) {
            cout << j << "
"; } cout << endl; } cout << endl; } template <typename T> void prt(vector<T>& arr, string msg = "") { cout << msg << " "; for (auto i: arr) { cout << i << " "; } cout << endl; } void prt_LCS(vector< vector<char> >& S, string& X, int i, int j) { // 遞迴呼叫 //cout << "i=" << i << " j=" << j << " " << S[i][j] << endl; if (i==0 || j == 0) return; if ('s' == S[i][j]) { prt_LCS(S, X, i-1, j-1); cout << X[i]; } else if ('j' == S[i][j]) { prt_LCS(S, X, i, j-1); } else { prt_LCS(S, X, i-1, j); } } void calc_LCS(string& X, string& Y) { cout << "X: " << X << endl; cout << "Y: " << Y << endl; vector< vector<int> > C; // 序列X[0..i] and Y[0..j]的公共子序列的長度 vector< vector<char> > S;  //最長公共子串的位置 for (int i=0; i<X.size(); i++) { C.push_back( vector<int>(Y.size()) );   S.push_back( vector<char>(Y.size()) ); } for (int i=0; i<X.size(); i++) C[i][0] = 0; for (int j=0; j<Y.size(); j++) C[0][j] = 0; for (int i=1; i<X.size(); i++) for (int j=1; j<Y.size(); j++) { if (X[i] == Y[j]) { C[i][j] = C[i-1][j-1] + 1; S[i][j] = 's'; // 相同,可以列印 } else if ( C[i][j-1] > C[i-1][j] ) { C[i][j] = C[i][j-1]; S[i][j] = 'j';  //Y的最後一個可以去掉,不影響LCS } else { C[i][j] = C[i-1][j] ; S[i][j] = 'i';  //X的最後一個可以去掉,不影響LCS } } prtM(C); prtM(S); prt_LCS(S, X, X.size()-1, Y.size()-1); cout<< endl; } int main() { string S1 = " ACCGGGTTAC"; // 為了方便表示,忽略第一個字元 string S2 = " AGGACCA"; calc_LCS(S1, S2); return 0; }