子串行的定義:給定乙個序列x=,另乙個序列z=,滿足如下條件時稱為x的子串行,
即存在乙個嚴格遞增的x的下標序列和所有的j=1,2,...,k,滿足zj=x[ik]下標序列,使得它們都相等。
如是x=的子串行。
給定兩個序列x和y,如果z既是x的子串行,也是y的子串行,我們稱它是x和y的公共子串行。
lcs問題:給定兩個序列,求x和y長度最長的公共子串行。
分析:lcs具有最優子結構性質,兩個序列的lcs包含兩個序列的字首的lcs。
定義c[0,...,m][0,....,n],m是x序列的長度,y序列的長度是n。c[i][j]的含義是序列x[0,...,i-1]和序列y[0,...,j-1]的lcs。
如果x[i-1]==y[j-1],則c[i][j]=c[i-1][j-1]+1;如果x[i-1]!=y[j-1],則序列x[0,...,i-1]和序列y[0,...,j-1]的lcs有兩種可能,
一種是序列x[0,...,i-2]和序列y[0,...,j-1]的lcs,另一種是序列x[0,...,i-1]和序列y[0,...,j-2]的lcs,兩者取較大值。
即c[i][j]=max(c[i][j-1],c[i-1][j])。
下面是實現的過程:
// lcs.cpp : 定義控制台應用程式的入口點。
它的時間複雜度是: o(m*n) 空間複雜度:o(m*n)//最長公共子串行問題:給定兩個序列x=
和y=,求x和y的最長公共子串行。
#include "stdafx.h"
#include
#include
#include
#include
using namespace std; //c[i][j]表示x的前i個字元和y的前j個字元所共有的序列長度;b[i][j]用來回溯得到的公共子串行 void lcs_length(string x,string y,vector
> & b,vector
> & c) else if(c[i-1][j]>=c[i][j-1]) else } //利用回溯和表b,追蹤lcs void print_lcs(vector
> & b, string x,int xlen, int ylen,string &s) else if(b[i][j]==1)//上面有c[i][j]=c[i-1][j];此時回溯c[i-1][j] print_lcs(b,x,i-1,j,s); else print_lcs(b,x,i,j-1,s); } int _tmain(int argc, _tchar* argv) lcs_length(x,y,b,c); print_lcs(b,x,m,n,s); cout<
進一步優化:
動態規劃解決lcs,一般要定義乙個表c[0,...,m][0,...,n],所用的空間複雜的是o(m*n),其實這裡表c的資訊是冗餘的。要想獲得c[i][j]其實,只需根據c[i-1][j-1],c[i][j-1],c[i-1][j]中的資訊獲得即可。我們可以用o(m)或者o(n)的空間
就可以獲得最長公共子串行的長度。我們這裡定義乙個陣列a[m+1],儲存如圖中綠色部分的資訊,a[0]=c[i-1][j-1],
當進行到比較x[i]與y[j]時,我們要得到c[i,j]的值,它需要用到c[i-1][j-1],c[i][j-1],c[i-1][j],即a[0],a[i],a[i-1]。同時,它是新的a[i],如第二張圖的紅色部分所示。同時,原來a[0]裡儲存的資料用過後,再下一次迴圈就用不上了,a[0]也需要更新。然後,這次迴圈結束後,就形成了乙個新的a陣列。我們可以看到,它是從上到下,一列列的生成c[i][j]中的元素的。注意,當生成了一列結束,即j>=m時,開始新的外一層迴圈,此時,設定a[0]=0.
下面的**在原有的基礎增加了乙個函式lcs(....),用o(m+1)的空間獲得lcs長度,這裡沒有恢復重構lcs中的元素。
// lcs.cpp : 定義控制台應用程式的入口點。//最長公共子串行問題:給定兩個序列x=
和y=,求x和y的最長公共子串行。
#include "stdafx.h"
#include
#include
#include
#include
using namespace std; //這裡對演算法進行了優化,空間複雜度o(m+1) void lcs(string x, string y, vector
& a) } } //c[i][j]表示x的前i個字元和y的前j個字元所共有的序列長度;b[i][j]用來回溯得到的公共子串行 void lcs_length(string x,string y,vector
> & b,vector
> & c) else if(c[i-1][j]>=c[i][j-1]) else } //利用回溯和表b,追蹤lcs void print_lcs(vector
> & b, string x,int xlen, int ylen,string &s) else if(b[i][j]==1)//上面有c[i][j]=c[i-1][j];此時回溯c[i-1][j] print_lcs(b,x,i-1,j,s); else print_lcs(b,x,i,j-1,s); } int _tmain(int argc, _tchar* argv) lcs_length(x,y,b,c); print_lcs(b,x,m,n,s); cout<
<
a(m+1); lcs(x,y ,a); cout<
<
動態規劃解決最長公共子串行和最長公共子串
找兩個字串的最長公共子串,這個子串要求在原字串中是連續的。而找兩個字串的最長公共子串行,只要求子串行的字元都在原字串中出現且保持相對順序不對 動態規劃 1.最長公共子串,s1 a1a 2a3.am s2 b 1b2b 3.b n 狀態轉移方程 記f i,j 是以a i 和 b j 結尾的字 符串的最...
動態規劃 最長公共子串行
問題描述 我們稱序列z z1,z2,zk 是序列x x1,x2,xm 的子串行當且僅當存在嚴格上公升的序列 i1,i2,ik 使得對j 1,2,k,有xij zj。比如z a,b,f,c 是x a,b,c,f,b,c 的子串行。現在給出兩個序列x和y,你的任務是找到x和y的最大公共子串行,也就是說要...
動態規劃 最長公共子串行
兩個序列的最長公共子序 lcs longest common length 的 每個字元可以不連續,如x y 那麼它們的最長公共子串行為。這是乙個經典的動態規劃問題,著手點還是找到 最精髓的 狀態轉移方程 假設x,y兩個序列的前i,j個位置的最大子串行已經找到為r i j 自底往上 那麼x i 與y...