小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

算法42(最長公共字串)

 白雪~~~ 2012-03-28
56.最長公共子序列。
題目:如果字符串一的所有字符按其在字符串中的順序出現(xiàn)在另外一個字符串二中,
則字符串一稱之為字符串二的子串。

注意,并不要求子串(字符串一)的字符必須連續(xù)出現(xiàn)在字符串二中。
請編寫一個函數(shù),輸入兩個字符串,求它們的最長公共子串,并打印出最長公共子串。
例如:輸入兩個字符串BDCABA和ABCBDAB,字符串BCBA和BDAB都是是它們的最長公共子串,
則輸出它們的長度4,并打印任意一個子串。

 

分析:求最長公共子串(Longest Common Subsequence, LCS)是一道非常經(jīng)典的動態(tài)規(guī)劃題,
因此一些重視算法的公司像MicroStrategy都把它當(dāng)作面試題。

完整介紹動態(tài)規(guī)劃將需要很長的篇幅,因此我不打算在此全面討論動態(tài)規(guī)劃相關(guān)的概念,只集中對LCS直接相關(guān)內(nèi)容作討論。如果對動態(tài)規(guī)劃不是很熟悉,請參考相關(guān)算法書比如算法討論。

 

先介紹LCS問題的性質(zhì):記Xm={x0, x1,…xm-1}和Yn={y0,y1,…,yn-1}為兩個字符串,而Zk={z0,z1,…zk-1}是它們的LCS,則:

1.       如果xm-1=yn-1,那么zk-1=xm-1=yn-1,并且Zk-1是Xm-1和Yn-1的LCS;
2.       如果xm-1≠yn-1,那么當(dāng)zk-1≠xm-1時(shí)Z是Xm-1和Y的LCS;
3.       如果xm-1≠yn-1,那么當(dāng)zk-1≠yn-1時(shí)Z是Yn-1和X的LCS;

 

下面簡單證明一下這些性質(zhì):
1.       如果zk-1≠xm-1,那么我們可以把xm-1(yn-1)加到Z中得到Z’,這樣就得到X和Y的一個長度為k+1的公共子串Z’。這就與長度為k的Z是X和Y的LCS相矛盾了。因此一定有zk-1=xm-1=yn-1。
既然zk-1=xm-1=yn-1,那如果我們刪除zk-1(xm-1、yn-1)得到的Zk-1,Xm-1和Yn-1,顯然Zk-1是Xm-1和Yn-1的一個公共子串,現(xiàn)在我們證明Zk-1是Xm-1和Yn-1的LCS。用反證法不難證明。假設(shè)有Xm-1和Yn-1有一個長度超過k-1的公共子串W,那么我們把加到W中得到W’,那W’就是X和Y的公共子串,并且長度超過k,這就和已知條件相矛盾了。
2.       還是用反證法證明。假設(shè)Z不是Xm-1和Y的LCS,則存在一個長度超過k的W是Xm-1和Y的LCS,那W肯定也X和Y的公共子串,而已知條件中X和Y的公共子串的最大長度為k。矛盾。
3.       證明同2。

 

有了上面的性質(zhì),我們可以得出如下的思路:

求兩字符串Xm={x0, x1,…xm-1}和Yn={y0,y1,…,yn-1}的LCS,如果xm-1=yn-1,那么只需求得Xm-1和Yn-1的LCS,并在其后添加xm-1(yn-1)即可;如果xm-1≠yn-1,我們分別求得Xm-1和Y的LCS和Yn-1和X的LCS,并且這兩個LCS中較長的一個為X和Y的LCS。

 

如果我們記字符串Xi和Yj的LCS的長度為c[i,j],我們可以遞歸地求c[i,j]:

          /      0                               if i<0 or j<0
c[i,j]=          c[i-1,j-1]+1                    if i,j>=0 and xi=xj
         /       max(c[i,j-1],c[i-1,j]           if i,j>=0 and xi≠xj

 

上面的公式用遞歸函數(shù)不難求得。但從前面求Fibonacci第n項(xiàng)(本微軟等100題系列第19題)的分析中我們知道直接遞歸會有很多重復(fù)計(jì)算,我們用從底向上循環(huán)求解的思路效率更高。

 

為了能夠采用循環(huán)求解的思路,我們用一個矩陣(參考代碼中的LCS_length)保存下來當(dāng)前已經(jīng)計(jì)算好了的c[i,j],當(dāng)后面的計(jì)算需要這些數(shù)據(jù)時(shí)就可以直接從矩陣讀取。另外,求取c[i,j]可以從c[i-1,j-1] 、c[i,j-1]或者c[i-1,j]三個方向計(jì)算得到,相當(dāng)于在矩陣LCS_length中是從c[i-1,j-1],c[i,j-1]或者c[i-1,j]的某一個各自移動到c[i,j],因此在矩陣中有三種不同的移動方向:向左、向上和向左上方,其中只有向左上方移動時(shí)才表明找到LCS中的一個字符。于是我們需要用另外一個矩陣(參考代碼中的LCS_direction)保存移動的方向。

 

參考代碼如下:
#include "string.h"

// directions of LCS generation
enum decreaseDir {kInit = 0, kLeft, kUp, kLeftUp};

/////////////////////////////////////////////////////////////////////////////
// Get the length of two strings' LCSs, and print one of the LCSs
// Input: pStr1         - the first string
//        pStr2         - the second string
// Output: the length of two strings' LCSs
/////////////////////////////////////////////////////////////////////////////
int LCS(char* pStr1, char* pStr2)
{
      if(!pStr1 || !pStr2)
            return 0;

      size_t length1 = strlen(pStr1);
      size_t length2 = strlen(pStr2);
      if(!length1 || !length2)
            return 0;

      size_t i, j;

      // initiate the length matrix
      int **LCS_length;
      LCS_length = (int**)(new int[length1]);
      for(i = 0; i < length1; ++ i)
            LCS_length[i] = (int*)new int[length2];

      for(i = 0; i < length1; ++ i)
            for(j = 0; j < length2; ++ j)
                  LCS_length[i][j] = 0;

 

      // initiate the direction matrix
      int **LCS_direction;
      LCS_direction = (int**)(new int[length1]);
      for( i = 0; i < length1; ++ i)
            LCS_direction[i] = (int*)new int[length2];

      for(i = 0; i < length1; ++ i)
            for(j = 0; j < length2; ++ j)
                  LCS_direction[i][j] = kInit;

      for(i = 0; i < length1; ++ i)
      {
            for(j = 0; j < length2; ++ j)
            {
                  if(i == 0 || j == 0)
                  {
                        if(pStr1[i] == pStr2[j])
                        {
                              LCS_length[i][j] = 1;
                              LCS_direction[i][j] = kLeftUp;
                        }
                        else
                              LCS_length[i][j] = 0;
                  }
                  // a char of LCS is found,
                  // it comes from the left up entry in the direction matrix
                  else if(pStr1[i] == pStr2[j])
                  {
                        LCS_length[i][j] = LCS_length[i - 1][j - 1] + 1;
                        LCS_direction[i][j] = kLeftUp;
                  }
                  // it comes from the up entry in the direction matrix
                  else if(LCS_length[i - 1][j] > LCS_length[i][j - 1])
                  {
                        LCS_length[i][j] = LCS_length[i - 1][j];
                        LCS_direction[i][j] = kUp;
                  }
                  // it comes from the left entry in the direction matrix
                  else
                  {
                        LCS_length[i][j] = LCS_length[i][j - 1];
                        LCS_direction[i][j] = kLeft;
                  }
            }
      }
      LCS_Print(LCS_direction, pStr1, pStr2, length1 - 1, length2 - 1);

      return LCS_length[length1 - 1][length2 - 1];
}

 

/////////////////////////////////////////////////////////////////////////////
// Print a LCS for two strings
// Input: LCS_direction - a 2d matrix which records the direction of
//                        LCS generation
//        pStr1         - the first string
//        pStr2         - the second string
//        row           - the row index in the matrix LCS_direction
//        col           - the column index in the matrix LCS_direction
/////////////////////////////////////////////////////////////////////////////
void LCS_Print(int **LCS_direction,
                    char* pStr1, char* pStr2,
                    size_t row, size_t col)
{
      if(pStr1 == NULL || pStr2 == NULL)
            return;

      size_t length1 = strlen(pStr1);
      size_t length2 = strlen(pStr2);

      if(length1 == 0 || length2 == 0 || !(row < length1 && col < length2))
            return;

      // kLeftUp implies a char in the LCS is found
      if(LCS_direction[row][col] == kLeftUp)
      {
            if(row > 0 && col > 0)
                  LCS_Print(LCS_direction, pStr1, pStr2, row - 1, col - 1);

            // print the char
            printf("%c", pStr1[row]);
      }
      else if(LCS_direction[row][col] == kLeft)
      {
            // move to the left entry in the direction matrix
            if(col > 0)
                  LCS_Print(LCS_direction, pStr1, pStr2, row, col - 1);
      }
      else if(LCS_direction[row][col] == kUp)
      {
            // move to the up entry in the direction matrix
            if(row > 0)
                  LCS_Print(LCS_direction, pStr1, pStr2, row - 1, col);
      }
}
擴(kuò)展:如果題目改成求兩個字符串的最長公共子字符串,應(yīng)該怎么求?子字符串的定義和子串的定義類似,但要求是連續(xù)分布在其他字符串中。比如輸入兩個字符串BDCABA和ABCBDAB的最長公共字符串有BD和AB,它們的長度都是2。

 //July注,更多可參考 算法導(dǎo)論一書第15章 動態(tài)規(guī)劃問題。
 //及我針對此題寫的一篇博文:24個經(jīng)典算法系列:3、動態(tài)規(guī)劃算法解一道面試題
 //http://blog.csdn.net/v_JULY_v/archive/2010/12/31/6110269.aspx
 //關(guān)于動態(tài)規(guī)劃算法,我日后還會在博客里清晰闡述。:D。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多