日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

动态规划之----最长公共子序列

發布時間:2025/3/16 编程问答 13 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态规划之----最长公共子序列 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

動態規劃算法的基本要素:

1)最優子結構

????? 當問題的最優解包含了其子問題的最優解時,稱該問題具有最優子結構性質。問題的最優子結構性質提供了該問題可用動態規劃算法求解的最重要線索。

????? 在動態規劃算法中,利用問題的最優字結構的性質,以自底向上的方式遞歸的從子問題的最優解逐步構造出整個問題的最優解。

2)重疊子問題

????? 可用動態規劃算法求解的問題應具備的另一基本要素是子問題的重疊性質。在用遞歸算法自頂向下求解此問題時,每次產生的子問題并不總是新問題,有些子問題被反復計算多次。動態規劃算法正是利用了這種子問題的重疊性質,對每個子問題只解一次,而后將其解保存在一個表格中,當再次需要解此問題時,只是簡單地用常熟時間查看一下結果。

3)備忘錄方法

???? 與動態規劃算法一樣,備忘錄方法用表格保存已解決的子問題的答案,在下次需要解此問題時,只要簡單的查看蓋子問題的解答,而不需要重新計算。與動態規劃算法不同的是,備忘錄方法的遞歸方式是自頂向下的,而動態規劃算法是自底向上遞歸的。


最長公共子序列(LCS)問題描述:

??? 給定兩個序列,找出在兩個序列中同時出現的最長子序列的長度。一個子序列是出現在相對順序的序列,但不一定是連續的。例如,“ABC”,“ABG”,“BDF”,“AEG”,“acefg“,..等都是”ABCDEFG“ 序列。因此,長度為n的字符串有2 ^ n個不同的可能的序列。

????? 注意最長公共子串(Longest CommonSubstring)和最長公共子序列(LongestCommon Subsequence, LCS)的區別:子串(Substring)是串的一個連續的部分,子序列(Subsequence)則是從不改變序列的順序,而從序列中去掉任意的元素而獲得的新序列;更簡略地說,前者(子串)的字符的位置必須連續,后者(子序列LCS)則不必。比如字符串acdfg同akdfc的最長公共子串為df,而他們的最長公共子序列是adf。LCS可以使用動態規劃法解決。

這是一個典型的計算機科學問題,基礎差異(即輸出兩個文件之間的差異文件比較程序),并在生物信息學有較多應用。

例子:
輸入序列“ABCDGH”和“AEDFHR” 的LCS是“ADH”長度為3。
輸入序列“AGGTAB”和“GXTXAYB”的LCS是“GTAB”長度為4。

這個問題的直觀的解決方案是同時生成給定序列的所有子序列,找到最長匹配的子序列。此解決方案的復雜性是指數的。讓我們來看看如何這個問題 (擁有動態規劃(DP)問題的兩個重要特性):

1)最優子結構:
設輸入序列是X [0 .. m-1]和Y [0 .. n-1],長度分別為m和n。和設序列 L(X [0 .. m-1],Y[0 .. n-1])是這兩個序列的LCS的長度。

以下為L(X [0 .. M-1],Y [0 .. N-1])的遞歸定義:

如果兩個序列的最后一個元素匹配(即X [M-1] == Y [N-1])
L(X [0 .. M-1],Y [0 .. N-1])= 1 + L(X [0 .. M-2],Y [0 .. N-1])

如果兩個序列的最后字符不匹配(即X [M-1]!= Y [N-1])
L(X [0 .. M-1],Y [0 .. N-1])= MAX(L(X [0 .. M-2],Y [0 .. N-1]),L(X [0 .. M-1],Y [0 .. N-2])

例子:

1)考慮輸入字符串“AGGTAB”和“GXTXAYB”。最后一個字符匹配的字符串。這樣的LCS的長度可以寫成:
L(“AGGTAB”, “GXTXAYB”) = 1 + L(“AGGTA”, “GXTXAY”)

2)考慮輸入字符串“ABCDGH”和“AEDFHR。最后字符不為字符串相匹配。這樣的LCS的長度可以寫成:
L(“ABCDGH”, “AEDFHR”) = MAX ( L(“ABCDG”, “AEDFHR”), L(“ABCDGH”, “AEDFH”) )

因此,LCS問題有最優子結構性質!

2)重疊子問題:
以下是直接的遞歸實現, ?遵循上面提到的遞歸結構。

/* 簡單的遞歸實現LCS問題 */ #include<stdio.h> #include<stdlib.h>int max(int a, int b);/* Returns length of LCS for X[0..m-1], Y[0..n-1] */ int lcs( char *X, char *Y, int m, int n ) {if (m == 0 || n == 0)return 0;if (X[m-1] == Y[n-1])return 1 + lcs(X, Y, m-1, n-1);elsereturn max(lcs(X, Y, m, n-1), lcs(X, Y, m-1, n)); }/* Utility function to get max of 2 integers */ int max(int a, int b) {return (a > b)? a : b; }/* 測試上面的函數 */ int main() {char X[] = "AGGTAB";char Y[] = "GXTXAYB";int m = strlen(X);int n = strlen(Y);printf("Length of LCS is %d\n", lcs( X, Y, m, n ) );getchar();return 0; }

??????? 上面直接的遞歸方法的時間復雜度為O(2 ^ n).(在最壞的情況下。X和Y不匹配的所有字符即LCS的長度為0)。
按照到上述的實現,下面是對輸入字符串“AXYT”和“AYZX”的部分遞歸樹:

lcs("AXYT", "AYZX")/ \lcs("AXY", "AYZX") lcs("AXYT", "AYZ")/ \ / \ lcs("AX", "AYZX") lcs("AXY", "AYZ") lcs("AXY", "AYZ") lcs("AXYT", "AY")

??????? 在上述部分遞歸樹,LCS(“AXY”,“AYZ”)被調用兩次。如果我們繪制完整的遞歸樹,那么我們可以看到,我們可以看到很多重復的調用。所以這個問題有重疊的子結構性質,可使用memoization的或打表來避免重新計算。

?????? 這里我們采用的是矩陣實現,也就是二維數組。

?????? 第一步:先計算最長公共子序列的長度。

?????? 第二步:根據長度,然后通過回溯求出最長公共子序列。

???? ? 現有兩個序列X={x1,x2,x3,...xi},Y={y1,y2,y3,....,yi},

????? 設一個C[i,j]: 保存Xi與Yj的LCS的長度。

????? 遞推方程為:

?????


下面是用動態規劃(打表)解決LCS問題:

#include <stdio.h> #include <string.h> #include <algorithm> using namespace std;#define INF 1005 char a[INF]; char b[INF]; short c[INF][INF];int main() {int t;scanf("%d", &t);while(t--){memset(c, 0, sizeof(c));scanf("%s\n%s", a+1,b+1);int n1 = strlen(a+1);int n2 = strlen(b+1);int m = n1>n2?n1:n2;for(int i=1; i<=n1; i++){for(int j=1; j<=n2; j++){if(a[i]==b[j]) c[i][j] = c[i-1][j-1] + 1;else if(c[i-1][j]>=c[i][j-1]) c[i][j] = c[i-1][j];else c[i][j] = c[i][j-1];}}printf("%d\n", c[n1][n2]);}return 0; } ????? 到這里,我們就用最經典的動態規劃方式解決了這個問題。

????? 但是,這個解法就是最好的嗎?能不能進一步優化呢?如果字符串的長度超過一萬,百萬,千萬,十億,一個十億級的二維表,對空間的要求也是相當恐怖的,我們能不能優化成一維表呢?

#include <stdio.h> #include <string.h> char s1[1001], s2[1001]; int dp[1001], t, old, tmp; int main(){scanf("%d", &t);while(t--){scanf("%s%s",s1,s2);memset(dp, 0, sizeof(dp));int lenS1=strlen(s1), lenS2=strlen(s2);for(int i=0; i<lenS1; i++){old=0; //若s1[i]==s2[j], dp[i][j] = dp[i-1][j-1]+1//否則,dp[i][j] = max(dp[i-1][j], dp[i][j-1])//此處進行了空間優化,old 代表 dp[i-1][j-1]//dp[j-1] 代表 dp[i][j-1], dp[j] 代表 dp[i-1][j]for(int j=0; j<lenS2; j++){tmp = dp[j];if(s1[i]==s2[j]) dp[j] = old+1;else if(dp[j-1]>dp[j]) dp[j]=dp[j-1];old = tmp;}}printf("%d\n", dp[lenS2-1]);}return 0; }

總結

以上是生活随笔為你收集整理的动态规划之----最长公共子序列的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。