最長公共子序列問題

子序列

一個序列A = a1,a2,……an,中任意刪除若干項线欲,剩余的序列叫做A的一個子序列。也可以認為是從序列A按原順序保留任意若干項得到的序列趴泌。

對序列 1,3,5,4,2,6,8,7來說氏仗,序列3,4,8,7 是它的一個子序列呐舔。
對于一個長度為n的序列,它一共有2^n 個子序列,有(2^n – 1)個非空子序列。
請注意:子序列不是子集,它和原始序列的元素順序是相關的

公共子序列

顧名思義盅视,如果序列C既是序列A的子序列,同時也是序列B的子序列,則稱它為序列A和序列B的公共子序列。

空序列是任何兩個序列的公共子序列

最長公共子序列

A和B的公共子序列中長度最長的(包含元素最多的)叫做A和B的公共子序列

最長公共子序列示意圖

描述:最長公共子序列問題就是求序列A= a_1,a_2,……a_x, 和B = b_1,b_2,……b_m,的一個最長公共子序列

A_x表示序列A的連續(xù)前x項構成的子序列埃撵,即A_x= a_1,a_2,……a_x, B_y= b_1,b_2,……b_y, 我們用LCS(x, y)表示它們的最長公共子序列長度谣拣,那原問題等價于求LCS(m,n)苛谷。為了方便我們用L(x, y)表示A_xB_y的一個最長公共子序列

  • x表示子序列考慮最后一項
    (1) A_x = B_y

    • 那么它們L(A_x, B_y)的最后一項一定是這個元素x锣尉,為了方便树瞭,我們令t = A_x = B_y, 我們用反證法:假設L(x,y)最后一項不是t凉敲,則要么L(x,y)為空序列(別忘了這個)阻塑,要么L(x,y)的最后一項是A_a=B_b ≠ t, 且顯然有a < x, b < y。無論是哪種情況我們都可以把t接到這個L(x,y)后面,從而得到一個更長的公共子序列菠隆。矛盾!

    • 如果我們從序列A_x中刪掉最后一項a_x得到A_{x-1},從序列B_y中也刪掉最后一項b_y得到B_{y-1}骇径,(多說一句角標為0時躯肌,認為子序列是空序列),則我們從L(x,y)也刪掉最后一項t得到的序列是L(x – 1, y - 1)破衔。為什么呢清女?和上面的道理相同,如果得到的序列不是L(x - 1, y - 1)晰筛,則它一定比L(x - 1, y - 1)短(注意L(嫡丙,)是個集合!)读第,那么它后面接上元素t得到的子序列L(x,y)也比L(x - 1, y - 1)接上元素t得到的子序列短曙博,這與L(x, y)是最長公共子序列矛盾。

    • 因此L(x, y) = L(x - 1, y - 1)最后接上元素t

    • LCS(A_x, B_y) = LCS(x - 1, y - 1) + 1

    (2) A_x ≠ B_y

    • 仍然設t = L(A_x, B_y), 或者L(A_x, B_y)是空序列(這時t是未定義值不等于任何值)怜瞒。則t ≠ A_xt ≠ B_y至少有一個成立父泳,因為t不能同時等于兩個不同的值
      (2.1)
      如果t ≠ A_x,則有L(x, y)= L(x - 1, y),因為根本沒A_x的事嘛惠窄。
      LCS(x,y) = LCS(x – 1, y)
      (2.2)
      如果t ≠ B_y,類似L(x, y)= L(x , y - 1)
      LCS(x,y) = LCS(x, y – 1)
    • 我們事先并不知道t逝她,由定義,我們取最大的一個睬捶,因此這種情況下,有LCS(x,y) = max(LCS(x – 1, y) , LCS(x, y – 1))
  • 目前得到的有:
    LCS(x,y) =
    (1) LCS(x - 1,y - 1) + 1 , 如果A_x = B_y
    (2) max(LCS(x – 1, y) , LCS(x, y – 1)), 如果A_x ≠ B_y

  • 一個空序列和任何序列的最長公共子序列都是空序列
    LCS(x,y) =
    (1) LCS(x - 1,y - 1) + 1 如果A_x = B_y
    (2)max(LCS(x – 1, y) , LCS(x, y – 1)) 如果A_x ≠ B_y
    (3) 0 如果x = 0或者y = 0

for x = 0 to n do
    for y = 0 to m do
        if (x == 0 || y == 0) then 
            LCS(x, y) = 0
        else if (Ax == By) then
            LCS(x, y) =  LCS(x - 1,y - 1) + 1
        else 
            LCS(x, y) = ) max(LCS(x – 1, y) , LCS(x, y – 1))
        endif
    endfor
endfor

求最大公共子序列

  • LCS(x,y)的值來源的三種情況
    • (1)LCS(x – 1, y – 1) + 1如果A_x = B_y黔宛,這對應L(x,y) = L(x- 1, y- 1)末尾接上A_x

    • (2.1) LCS(x – 1, y) 如果A_x ≠ B_yLCS(x – 1, y) ≥LCS(x, y – 1)
      這對應L(x,y)= L(x – 1, y)
      (2.2)LCS(x, y – 1) 如果A_x ≠ B_yLCS(x – 1, y) <LCS(x, y – 1)
      這對應L(x,y) = L(x, y – 1)

    • (3) 0 如果 x =0或者y = 0 ,這對應L(x,y)=空序列

  • LCS(x – 1, y) = LCS(x, y – 1)時擒贸,其實走哪個分支都一樣臀晃,雖然長度時一樣的,但是可能對應不同的子序列介劫,所以最長公共子序列并不唯一
//用來記錄Xi和Yj的最長公共子序列的長度
private int[][] c;
//用來標識Xi和Yi的最長公共子序列是由哪種情況得來:c[i][j-1]徽惋、c[i-1][j]、c[i][j]+1座韵。 
//該數組能還原出最長公共子序列
private int[][] s;
void LCSLength(String a, String b){
   // x和y的最前端分別加上0
   char[] x = ("0"+a).toCharArray();
   char[] y = ("0"+b).toCharArray();

   c = new int[x.length][y.length];
   s = new int[x.length][y.length];

   // 初始化c险绘、s
   for( int i=0; i<x.length; i++ ){
       c[i][0] = 0;
   }
   for( int i=0; i<y.length; i++ ){
       c[0][i] = 0;
   }

   // 從上到下、從左到右填充c誉碴、s數組
   for( int i=1; i<x.length; i++ ){
       for( int j=1; j<y.length; j++ ){
           if( x[i]==y[j] ){
               c[i][j] = c[i-1][j-1]+1;
               s[i][j] = 1;
           }
           else if ( c[i-1][j] >= c[i][j-1] ){
               c[i][j] = c[i-1][j];
               s[i][j] = 2;
           }
           else {
               c[i][j] = c[i][j-1];
               s[i][j] = 3;
           }
       }
   }
}

//根據s數組求最大公共子序列
StringBuilder sb = new StringBuilder();

void CLCS( int i, int j ){
   if ( i==0 || j==0 ) return;
   if ( s[i][j]==1 ) {
       CLCS( i-1,j-1 );
       sb.append( x[i] ); // 為了讓公共子序列正序輸出宦棺,因此需要在遞歸調用之后將x[i]添加至sb
   }
   else if ( s[i][j]==2 ){
       CLCS( i-1,j );
   }
   else {
       CLCS( i,j-1 );
   }
}

原理和代碼總結來源于:

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市黔帕,隨后出現(xiàn)的幾起案子代咸,更是在濱河造成了極大的恐慌,老刑警劉巖成黄,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呐芥,死亡現(xiàn)場離奇詭異,居然都是意外死亡奋岁,警方通過查閱死者的電腦和手機思瘟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闻伶,“玉大人滨攻,你說我怎么就攤上這事∠汗ィ” “怎么了铡买?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長霎箍。 經常有香客問我奇钞,道長,這世上最難降的妖魔是什么漂坏? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任景埃,我火速辦了婚禮媒至,結果婚禮上,老公的妹妹穿的比我還像新娘谷徙。我一直安慰自己拒啰,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布完慧。 她就那樣靜靜地躺著谋旦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪屈尼。 梳的紋絲不亂的頭發(fā)上册着,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音脾歧,去河邊找鬼甲捏。 笑死,一個胖子當著我的面吹牛鞭执,可吹牛的內容都是我干的司顿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼兄纺,長吁一口氣:“原來是場噩夢啊……” “哼大溜!你這毒婦竟也來了?” 一聲冷哼從身側響起囤热,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤猎提,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后旁蔼,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡疙教,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年棺聊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贞谓。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡限佩,死狀恐怖,靈堂內的尸體忽然破棺而出裸弦,到底是詐尸還是另有隱情祟同,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布理疙,位于F島的核電站晕城,受9級特大地震影響,放射性物質發(fā)生泄漏窖贤。R本人自食惡果不足惜砖顷,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一贰锁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧滤蝠,春花似錦豌熄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至览闰,卻和暖如春芯肤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背焕济。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工纷妆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晴弃。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓掩幢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親上鞠。 傳聞我的和親對象是個殘疾皇子际邻,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內容