最長公用子串LCS–之動態規劃

來源:互聯網
上載者:User

最長公用子串--longest common Subsequence,即兩個字串中連續字串的的公用子串。

描述:

給定兩個字串X=<x1,  x2, … ,  xm>, Y=<y1, y2, ... , yn>, 假設公用子串Z=<z1, z2, ... zk> 滿足:

  • 如果xm = yn,那麼zk = xm = yn而且Zk-1是Xm-1和Yn-1的一個LCS
  • 如果xm ≠ yn,那麼zk ≠ xm → Z是Xm-1和Y的一個LCS
  • 如果xm ≠ yn,那麼zk ≠ yn → Z是X和Yn-1的一個LCS

上面說明X,Y的LCS子串具有最優子結構。遞推公式如下:設c[i,j]為最長子串長度

 過程:

1、把兩個字串一個橫排放置,一個縱排放置。如字串longstr=“acebb”, Y=“acttacebd”,此時形成了一個矩陣arr【】【】。

2、當longStr【i】== shortStr【j】,時然後我們就可以在橫、豎的矩陣中相應位置arr【i】【j】位置置1,


矩陣中對應位置置1

看這,我們想要求最長子串,我們要做的:

第一步:遍曆長、短字串,然後在矩陣數組中相同位置置1

第二步:然後數斜線長度,最長即為結果。

代碼如下:

#include <string>#include <assert.h>using namespace std;char* LCS(const char* shortStr, const char* longStr);int _tmain(int argc, _TCHAR* argv[]){const char* shortStr = "acettab";const char* longStr  = "acettabfaceabcccctaf";char* result = LCS(shortStr, longStr);printf("longest common substring is: %s", result);getchar();  //在使用vs2008 vs2010時,使程式暫停下來return 0;}char* LCS(const char* shortStr, const char* longStr){assert(shortStr && longStr);int maxlen = 0;int row = 0;        //計算兩字串長度unsigned slen = strlen( shortStr );unsigned llen = strlen( longStr );        //定義二維數組,即矩陣unsigned **arr = NULL;        //初始化二維矩陣arr = (unsigned ** ) malloc ( llen * sizeof(unsigned*));memset(arr, 0, llen * sizeof(unsigned*));for(unsigned i = 0; i < llen; ++i){arr[i] = (unsigned *) malloc(slen * sizeof(unsigned));memset(arr[i], 0, slen * sizeof(unsigned));}        //如,比較橫、豎串,當字元相等時,矩陣對應位置置1,此時記錄最長子串長度,賦值給maxlen,並記錄最長子串結束的結束位置row        //row值方便計運算元串      for(unsigned i = 0; i < llen ; ++i){for (unsigned j = 0; j < slen; ++j){if(shortStr[j] == longStr[i]){if (i == 0 || j == 0){arr[i][j] = 1;}elsearr[i][j] = arr[i-1][j-1] + 1;}if (arr[i][j] > maxlen){maxlen = arr[i][j];row = i + 1;}}}       //釋放資源for(int i = 0; i < slen; ++i)free(arr[i]);free(arr);char *lcsResult = NULL;lcsResult = (char*) malloc (maxlen * sizeof(char) + 1);memset(lcsResult, 0, maxlen + 1);int t;int tmp = maxlen;for (t = 0; t < maxlen; ++t){*(lcsResult + t) = *(longStr + row - tmp);//計算最長子串tmp -= 1;}lcsResult[t] = '\0';return lcsResult;}

時間複雜度:O(m*n), 空間複雜度O(m*n)


圖2:計數遞增的置數

改進:

正如上面代碼所示,使用了二維矩陣記錄每個相等的位置和子串的長度,空間複雜度為o(m*n)。從遞推公式得知中我們計算最長子串的下一個連續位置時,即C【i,j】時使用的僅僅和C【i-1,j】和C【i, j-1】,或者C【i-1, j-1】有關。那我們可以可以僅僅使用C【i-1,j】,C【i,j】兩行即可。

代碼如下:

#include <string>#include <assert.h>using namespace std;char* LCS(const char* shortStr, const char* longStr);int _tmain(int argc, _TCHAR* argv[]){const char* shortStr = "a2cett4ab";const char* longStr  = "aceabfacett3abcccctaf";char* result = LCS(shortStr, longStr);printf("longest common substring is: %s", result);getchar();return 0;}char* LCS(const char* shortStr, const char* longStr){assert(shortStr && longStr);int maxlen = 0;int row = 0;unsigned slen = strlen( shortStr );unsigned llen = strlen( longStr );unsigned **arr = NULL;arr = (unsigned ** ) malloc ( 2 * sizeof(unsigned*));memset(arr, 0, 2 * sizeof(unsigned*));for(unsigned i = 0; i < 2; ++i)                                  //僅僅申請了兩行資料{arr[i] = (unsigned *) malloc(slen * sizeof(unsigned));memset(arr[i], 0, slen * sizeof(unsigned));}for (unsigned j = 0; j < slen; ++j){if (shortStr[j] == longStr[0]){arr[0][j] = 1;}}for(int p = 0; p < slen ; ++p)printf(" %d", arr[0][p]);printf("\n");for(unsigned i = 1; i < llen ; ++i){for (unsigned j = 0; j < slen; ++j){if(shortStr[j] == longStr[i]){if (j == 0){arr[1][j] = 1;}elsearr[1][j] = arr[0][j-1] + 1;}if (arr[1][j] > maxlen){maxlen = arr[1][j];row = i + 1;}}memcpy(arr[0], arr[1], slen * sizeof(unsigned));memset(arr[1], 0, slen * sizeof(unsigned));for(int p = 0; p < slen ; ++p)                           //輸出printf(" %d", arr[0][p]);printf("\n");}for(int i = 0; i < 2; ++i)free(arr[i]);free(arr);char *lcsResult = NULL;lcsResult = (char*) malloc (maxlen * sizeof(char) + 1);memset(lcsResult, 0, maxlen + 1);int t;int tmp = maxlen;for (t = 0; t < maxlen; ++t){*(lcsResult + t) = *(longStr + row - tmp);tmp -= 1;}lcsResult[t] = '\0';return lcsResult;}

時間複雜度: O(m*n),空間複雜度: O(2*n)

學習思考:在使用動態規劃求最大公用子串LCS時,需要較大的記憶體開銷,即O(m*n),時間複雜度也是O(m*n),從遞推公式中我們發現可以把空間複雜度降低到O(2*n)。是一個很大的改進。那時間複雜度有沒有更好的最佳化呢?尾碼樹提供了一種更優的時間複雜度O(m)。以此為契機,學習尾碼樹。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.