子串行形式化定義:
給定乙個序列x=,另乙個序列z=,若存在乙個嚴格遞增的x的下標序列對所有的1,2,3,...,k,都滿足x(ik)=zk,則稱z是x的子串行
比如z=是x=的子串行
公共子串行定義:
如果z既是x的子串行,又是y的子串行,則稱z為x和y的公共子串行
最長公共子串行(以下簡稱lcs):
2個序列的子串行中長度最長的那個
蠻力法求解最長公共子串行:
需要遍歷出所有的可能,時間複雜度是o(n³),太慢了
動態規劃求解最長公共子串行:
分析規律:
設x=,y=為兩個序列,z=是他們的任意公共子串行
經過分析,我們可以知道:
1、如果xm = yn,則zk = xm = yn 且 zk-1是xm-1和yn-1的乙個lcs
2、如果xm != yn 且 zk != xm,則z是xm-1和y的乙個lcs
3、如果xm != yn 且 zk != yn,則z是x和yn-1的乙個lcs
所以如果用乙個二維陣列c表示字串x和y中對應的前i,前j個字元的lcs的長度話,可以得到以下公式:
文字意思就是:
設p1表示x的前 i-1 個字元和y的前 j 個字元的lcs的長度
p2表示x的前 i 個字元和y的前 j-1 個字元的lcs的長度
p表示x的前 i-1 個字元和y的前 j-1 個字元的lcs的長度
p0表示x的前 i 個字元和y的前 j 個字元的lcs的長度
如果x的第 i 個字元和y的第 j 個字元相等,則p0 = p + 1
如果x的第 i 個字元和y的第 j 個字元不相等,則p0 = max(p1,p2)
做法:因此,我們只需要從c[0][0]開始填表,填到c[m-1][n-1],所得到的c[m-1][n-1]就是lcs的長度
但是,我們怎麼得到lcs本身而非lcs的長度呢?
也是用乙個二維陣列b來表示:
在對應字元相等的時候,用↖標記
在p1 >= p2的時候,用↑標記
在p1 < p2的時候,用←標記
偽**:
若想得到lcs,則再遍歷一次b陣列就好了,從最後乙個位置開始往前遍歷:
如果箭頭是↖,則代表這個字元是lcs的一員,存下來後 i-- , j--
如果箭頭是←,則代表這個字元不是lcs的一員,i--
如果箭頭是↑ ,也代表這個字元不是lcs的一員,j--
如此直到i = 0或者j = 0時停止,最後存下來的字元就是所有的lcs字元
比如說求abcbdab和bdcaba的lcs:
灰色且帶↖箭頭的部分即為所有的lcs的字元
下面演示下c陣列的填表過程:(以求abcb和bdca的lcs長度為例):
以此類推
最後填出的表為:
右下角的2即為lcs的長度
時間複雜度:
由於只需要填乙個m行n列的二維陣列,其中m代表第乙個字串長度,n代表第二個字串長度
所以時間複雜度為o(m*n)
**:#include
#include
#include
using namespace std;
void lcs(string s1,string s2)
else}}
for(int i=0;isame; //存lcs字元
stacksame1,same2; //存lcs字元在字串1和字串2中對應的下標,方便顯示出來
for(int i = m-1,j = n-1;i >= 0 && j >= 0; )
else if(b[i][j] == 2)
i--;
else
j--;
}cout cout<<' '; }cout cout<<' '; }cout cout delete b; }int main() 結果示例: 1.描述 給定兩個序列 x y 求x和y的乙個最長公共子串行。2.分析 設最長子序列 z 則 1 若 xm yn 則 zk xm yn,且z k 1 是 x m 1 和 y n 1 的最長公共子串行 2 若 xm yn 且 zk xm 則 z 是 x m 1 和 y 的最長公共子串行 3 若 xm ... 首先,動態規劃的關鍵是將之前所計算的結果儲存起來,之後直接呼叫!1.問題描述 字串的子串行 是指從該字串中去掉任意多個字元後剩下的字元在不改變順序的情況下組成的新字串。最長公共子串行 是指多個字串可具有的長度最大的公共的子串行。比如 adbcbd bdcaba這兩個字串的最長公共子串行是dcb 2.... 最長公共子串行也是動態規劃中的乙個經典問題。有兩個字串 s1 和 s2,求乙個最長公共子串,即求字串 s3,它同時為 s1 和 s2 的子串,且要求它的長度最長,並確定這個長度。這個問題被我們稱為最長公共子串行問題。與求最長遞增子串行一樣,我們首先將原問題分割成一些子問題,我們用 dp i j 表示...最長公共子串行LCS(動態規劃)
最長公共子串行(LCS) 動態規劃
動態規劃 最長公共子串行(LCS)