這個世界上根本就不存在「不會做」這回事,當你失去了所有的依靠的時候,自然就什麼都會了。最長公共子串行的問題常用於解決字串的相似度,是乙個非常實用的演算法,作為碼農,此演算法是我們的必備基本功。最長公共子串(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的字串 輸入全是大寫英文...