動態規劃之最長公共子串行

2022-03-06 17:57:44 字數 4127 閱讀 4882

這個世界上根本就不存在「不會做」這回事,當你失去了所有的依靠的時候,自然就什麼都會了。

最長公共子串行的問題常用於解決字串的相似度,是乙個非常實用的演算法,作為碼農,此演算法是我們的必備基本功。最長公共子串(longest common substirng)和最長公共子串行(longest common subsequence,lcs)的區別為:子串是串的乙個連續的部分,子串行則是從不改變序列的順序,而從序列中去掉任意的元素而獲得新的序列;也就是說,子串中字元的位置必須是連續的,子串行則可以不必連續。

問題描述: 字串行的子串行是指從給定字串行中隨意地(不一定連續)去掉若干個字元(可能乙個也不去掉)後所形成的字串行。令給定的字串行x=「x0,x1,…,xm-1」,序列y=「y0,y1,…,yk-1」是x的子串行,存在x的乙個嚴格遞增下標序列

2.1 刻畫最長公共子串行的最優子結構的特徵

如果使用暴力搜尋求解lcs問題,需要窮舉x的所有子串行,對每乙個子串行檢查它是否是y的子串行,記錄找到的最長的子串行。x的每個子串行對應的x的下標集合是(1

,2,3

,...

..m)

的乙個子集,所以有2m

個子序列。但是最長公共子串行有最優子結構的性質。子問題的自然分類對應兩個輸入序列的「字首」對。

定理: 考慮最長公共子串行問題如何分解成子問題,設a=「a1,…,am」,b=「b1,…,bn」,並z=「z1,…,zk」為它們的最長公共子串行。不難證明有以下性質:

(1) 如果am=bn,則zk=am=bn,且「z1,…,zk-1」是「a1,…,am-1」和「b1,…,bn-1」的乙個最長公共子串行;

(2) 如果am!=bn,則若zk!=am,蘊涵「z1,…,zk」是「a1,…,am-1」和「b1,…,bn」的乙個最長公共子串行;

(3) 如果am!=bn,則若zk!=bn,蘊涵「z1,…,zk」是「a1,…,am」和「b1,…,bn-1」的乙個最長公共子串行。

上面的定理告訴我們:兩個序列的lcs包含兩個序列字首的lcs,因此lcs問題具有最優子結構的性質。

2.2 構建遞迴解

通過上面的定理我們可以發現在求a=

「a1,

…,am

」,b=

「b1,

…,bn

」的乙個公共的lcs的時候我們需要求解乙個到兩個子問題。、

如果am=bn,我們應該求am

−1和b

n−1的lcs;

如果am!=bn,若zk!=am,我們應該求am

−1和b

n的lcs。如果am!=bn,則若zk!=bn,我們應該求am

和bn−

1的lcs。兩者lcs比較長的稱為a和b的lcs。

我們可以很容易的發現子問題重疊的性質。在求a和b的lcs的過程中,我們的2種情況,都存在am

−1和b

n−1的lcs的重疊子問題。設計lcs的遞迴演算法首先需要建立最優解的遞迴式,c[i,j]表示ai和bj的lcs長度,如果i=j=0,那麼c[i,j]=0.根據lcs問題最優子結構的性質得出以下公式: c[

i,j]

=⎧⎩⎨

0c[i

−1,j

−1]+

1max

(c[i

,j−1

],c[

i−1,

j])若

i=0或

者j=0

若i,j>0

且ai = bj

若i,j>0

且ai!=bj

在上面的問題中我們通過限制條件限定了需要求解哪些子問題。在前面的演算法中我們沒有限定排除任何子問題,在這裡我們需要根據限制條件排除相應的子問題。

2.3 計算lcs的長度(求最優解)

根據2.2的遞迴公式,我們可以很痛以的寫出乙個指數時間的遞迴演算法。但是,由於lcs問題只有θ(

mn)個不同的子問題,我們可以使用dp來自底向上的計算。

演算法偽**:

lcs-length(x,y)

for(j=0 to n)

/**i是行 j是列

*/for(i=1 to m)

elseif(c[i-1,j]>=c[i,j-1])else}}

return c and b;

}

2.4 構造lcs(構造最優解)

我們可以使用輔助表b來快速構造x和y的lcs,只需要從b[m,n]開始按照箭頭的方向追蹤即可.遞迴演算法如下:

print_lcs(x,b,i,j)

if(b[i,j]=="\")else

if(b[i,j]=="|")else

}

演算法實現類

package lbz.ch15.dp.ins3;

/** *@author lbzhang

*@description 最大公共子串行

*/public

class

lcs

for (j = 0; j <= n; j++)

for (i = 1; i <= m; i++) else

if (c[i - 1][j] >= c[i][j - 1]) else }}

return c;

}/**

* 為了輸出最長公共子串行,改進的輸出

*@param x

*@param y

*@return

*/public

static

char lcsprint(object x, object y)

for (j = 0; j <= n; j++)

for (i = 1; i <= m; i++) else

if (c[i - 1][j] >= c[i][j - 1]) else }}

return b;

}// ///print the lcs

// //採用遞迴的方式將結果列印出來

public

static

void

printlcs(int c, object x, object y, int i, int j)

if (x[i - 1].equals(y[j - 1])) else

if (c[i - 1][j] >= c[i][j - 1]) else

}public

static

void

printbysignallcs(char b, object x, int i, int j)

if (b[i][j]=='\\') else

if (b[i][j]=='|') else }}

測試類

package lbz.ch15.dp.ins3;

/**

*@author lbzhang

*@description 最長公共子串行測試類

*/public

class

test ,

y = ;

integer a = , b = ;

int c;

char p;

c = lcs.lcslength(x, y);

//p=lcs.lcsprint(x, y);

lcs.printlcs(c, x, y, 29, 28);

system.out.println();

c = lcs.lcslength(a, b);//二維表

lcs.printlcs(c, a, b, 8, 8);

system.out.println();

system.out.println("--------******改進******---------");

p=lcs.lcsprint(x, y);

lcs.printbysignallcs(p, x, 29, 28);

system.out.println();

p=lcs.lcsprint(a, b);

lcs.printbysignallcs(p, a, 8, 8);}}

實驗結果

動態規劃之最長公共子串行

最長公共子串行簡介 舉例說明並分析 塊測試結果 乙個給定序列的子串行是在該序列中刪去若干元素後得到的序列,確切的說,若給定序列x 則另一串行z x的子串行是指存在乙個嚴格的下標序列,使得對於所有的j 0,1,k 1有zj xij。例如序列z 是序列x 的子串行,相應的遞增下標序列維。最長公共子串行問...

動態規劃之最長公共子串行

給出兩個字串,求出這樣的一 個最長的公共子串行的長度 子串行 中的每個字元都能在兩個原串中找到,而且每個字元的先後順序和原串中的 先後順序一致。sample input abcfbc abfcab programming contest abcd mnp sample output 4 2 0對於動...

動態規劃之最長公共子串行

最長公共子串行問題 time limit 1000 ms memory limit 65536 kib submit statistic discuss problem description 給定兩個序列x input 輸入資料有多組,每組有兩行 每行為乙個長度不超過500的字串 輸入全是大寫英文...