最長公共子串行

2021-06-29 08:53:30 字數 3605 閱讀 6149

乙個字串s,去掉零個或者多個元素所剩下的子串稱為s的子串行。最長公共子串行就是尋找兩個給定序列的子串行,該子串行在兩個序列中以相同的順序出現,但是不必要是連續的。

例如序列x=abcbdab,y=bdcaba。序列bca是x和y的乙個公共子串行,但是不是x和y的最長公共子串行,子串行bcba是x和y的乙個lcs,序列bdab也是。

尋找lcs的一種方法是列舉x所有的子串行,然後注意檢查是否是y的子串行,並隨時記錄發現的最長子序列。假設x有m個元素,則x有2^m個子序列,指數級的時間,對長序列不實際。

使用動態規劃求解這個問題,先尋找最優子結構。設x=1,x2,…,xm>和y=1,y2,…,yn>為兩個序列,lcs(x,y)表示x和y的乙個最長公共子串行,可以看出

如果xm=yn,則lcs ( x,y ) = xm + lcs ( xm-1,yn-1 )。

如果xm!=yn,則lcs( x,y )= max

lcs問題也具有重疊子問題性質:為找出x和y的乙個lcs,可能需要找x和yn-1的乙個lcs以及xm-1和y的乙個lcs。但這兩個子問題都包含著找xm-1和yn-1的乙個lcs,等等.

dp最終處理的還是數值(極值做最優解),找到了最優值,就找到了最優方案;為了找到最長的lcs,我們定義dp[i][j]記錄序列lcs的長度,合法狀態的初始值為當序列x的長度為0或y的長度為0,公共子串行lcs長度為0,即dp[i][j]=0,所以用i和j分別表示序列x的長度和序列y的長度,狀態轉移方程為

dp[i][j] = 0  如果i=0或j=0

dp[i][j] = dp[i-1][j-1] + 1  如果x[i-1] = y[i-1]

dp[i][j] = max  如果x[i-1] != y[i-1]

動態規劃法

經常會遇到複雜問題不能簡單地分解成幾個子問題,而會分解出一系列的子問題。簡單地採用把大問題分解成子問題,並綜合子問題的解匯出大問題的解的方法,問題求解耗時會按問題規模呈冪級數增加。

為了節約重複求相同子問題的時間,引入乙個陣列,不管它們是否對最終解有用,把所有子問題的解存於該陣列中,這就是動態規劃法所採用的基本方法。

【問題】 

求兩字串行的最長公共字元子串行

問題描述:字串行的子串行是指從給定字串行中隨意地(不一定連續)去掉若干個字元(可能乙個也不去掉)後所形成的字串行。令給定的字串行x=

「x0,x1

,…,xm-1」,序列y=

「y0,y1

,…,yk-1」是x

的子串行,存在x

的乙個嚴格遞增下標序列,i1

,…,ik-1>

,使得對所有的j=0

,1,…,k-1

,有xij=yj

。例如,x=

「abcbdab

」,y=

「bcdb

」是x的乙個子串行。

考慮最長公共子串行問題如何分解成子問題,設a=

「a0,a1

,…,am-1」,b=

「b0,b1

,…,bm-1」,並z=

「z0,z1

,…,zk-1」為它們的最長公共子串行。不難證明有以下性質: (1

) 如果am-1=bn-1

,則zk-1=am-1=bn-1

,且「z0

,z1,…,zk-2」是「a0

,a1,…,am-2」和「b0

,b1,…,bn-2」的乙個最長公共子串行; (2

) 如果am-1!=bn-1

,則若zk-1!=am-1

,蘊涵「z0

,z1,…,zk-1」是「a0

,a1,…,am-2」和「b0

,b1,…,bn-1」的乙個最長公共子串行; (3

) 如果am-1!=bn-1

,則若zk-1!=bn-1

,蘊涵「z0

,z1,…,zk-1」是「a0

,a1,…,am-1」和「b0

,b1,…,bn-2」的乙個最長公共子串行。

這樣,在找a

和b的公共子串行時,如有am-1=bn-1

,則進一步解決乙個子問題,找「a0

,a1,…,am-2」和「b0

,b1,…,bm-2」的乙個最長公共子串行;如果am-1!=bn-1

,則要解決兩個子問題,找出「a0

,a1,…,am-2」和「b0

,b1,…,bn-1」的乙個最長公共子串行和找出「a0

,a1,…,am-1」和「b0

,b1,…,bn-2」的乙個最長公共子串行,再取兩者中較長者作為a

和b的最長公共子串行。

求解:引進乙個二維陣列c,用c[i][j]記錄x[i]與y[j] 的lcs 的長度,b[i][j]記錄c[i][j]是通過哪乙個子問題的值求得的,以決定搜尋的方向。

我們是自底向上進行遞推計算,那麼在計算c[i,j]之前,c[i-1][j-1],c[i-1][j]與c[i][j-1]均已計算出來。此時我們根據x[i] = y[j]還是x[i] != y[j],就可以計算出c[i][j]。

問題的遞迴式寫成:

回溯輸出最長公共子串行過程:

演算法分析:

由於每次呼叫至少向上或向左(或向上向左同時)移動一步,故最多呼叫(m + n)次就會遇到i = 0或j = 0的情況,此時開始返回。返回時與遞迴呼叫時方向相反,步數相同,故演算法時間複雜度為θ(m + n)。

[cpp]view plain

copy

#include "iostream"

#include "string"

#include "vector"

using

namespace

std;  

std::vector

> common;  

intlcs(string a,string b)  

for(

inti = 1; i <= a.size(); ++i)  

else

if(len[i-1][j] >= len[i][j-1])  

else

}  }  int

apos = a.size();  

intbpos = b.size();  

intcommonlen = len[apos][bpos];  

intk = commonlen;  

common.resize(commonlen);  

while

(apos && bpos)else

if(len[apos-1][bpos] >= len[apos][bpos-1])  

else

}  for(

inti = 0;i 

cout

commonlen;  

}  int

main(

intargc, 

char

const

*argv)  

最長公共子串行 最長公共子串

1 最長公共子串行 採用動態規劃的思想,用乙個陣列dp i j 記錄a字串中i 1位置到b字串中j 1位置的最長公共子串行,若a i 1 b j 1 那麼dp i j dp i 1 j 1 1,若不相同,那麼dp i j 就是dp i 1 j 和dp i j 1 中的較大者。class lcs el...

最長公共子串行 最長公共子串

1.區別 找兩個字串的最長公共子串,這個子串要求在原字串中是連續的。而最長公共子串行則並不要求連續。2 最長公共子串 其實這是乙個序貫決策問題,可以用動態規劃來求解。我們採用乙個二維矩陣來記錄中間的結果。這個二維矩陣怎麼構造呢?直接舉個例子吧 bab 和 caba 當然我們現在一眼就可以看出來最長公...

最長公共子串 最長公共子串行

子串要求連續 子串行不要求連續 之前的做法是dp求子序列 include include include using namespace std const int inf 0x3f3f3f3f const int mod 1000000007 string s1,s2 int dp 1010 10...