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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

动态规划套路在最长公共子串、最长公共子序列和01背包问题中的应用

發(fā)布時(shí)間:2025/5/22 编程问答 66 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态规划套路在最长公共子串、最长公共子序列和01背包问题中的应用 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>

適合動(dòng)態(tài)規(guī)劃(DP,dynamic programming)方法的最優(yōu)化問(wèn)題有兩個(gè)要素:最優(yōu)子結(jié)構(gòu)和重疊子問(wèn)題。

最優(yōu)子結(jié)構(gòu)指的是最優(yōu)解包含的子問(wèn)題的解也是最優(yōu)的。

重疊子問(wèn)題指的是每次產(chǎn)生的子問(wèn)題并不總是新問(wèn)題,有些子問(wèn)題會(huì)被重復(fù)計(jì)算多次。

所以可以用如下步驟來(lái)解決:

  • 遞歸定義最優(yōu)解計(jì)算公式
  • 構(gòu)造dp矩陣,按照公式逐步計(jì)算。
  • 接下來(lái)我們分別在最長(zhǎng)公共子串、最長(zhǎng)公共子序列和01背包問(wèn)題中演示如何應(yīng)用上面的套路。

    開(kāi)始討論之前要明白子串和子序列的區(qū)別在于:子串要求在原字符串中是連續(xù)的,而子序列則沒(méi)有要求[1]。例如, 字符串 s1=abcde,s2=ade,則LCStr=de,LCSeq=ade。

    其中LCStr表示Longest Common Substring 。LCSeq表示Longest Common Subsequence。

    1 最長(zhǎng)公共子串

    問(wèn)題定義

    對(duì)于兩個(gè)字符串,請(qǐng)?jiān)O(shè)計(jì)一個(gè)時(shí)間復(fù)雜度為O(m*n)的算法(這里的m和n為兩串的長(zhǎng)度),求出兩串的最長(zhǎng)公共子串的長(zhǎng)度。 給定兩個(gè)字符串A和B,同時(shí)給定兩串的長(zhǎng)度n和m。

    測(cè)試樣例:"1AB2345CD",9,"12345EF",7

    返回:4

    1.1 遞歸公式

    假設(shè)有字符串x,y

    f(i,j)表示x中以x[i]結(jié)尾的子串集合和y中以y[j]結(jié)尾的子串集合,兩者交集中最長(zhǎng)串的長(zhǎng)度。(i和j都在合法范圍內(nèi))

    比如x="caba",以a[2]結(jié)尾的子串集合如下: { "b","ab","cab"}

    當(dāng)x[i]!=y[j]時(shí),毫無(wú)疑問(wèn),交集為空集,此時(shí)f(i,j) = 0

    當(dāng)x[i]==y[j]時(shí),f(i,j) = f(i-1,j-1) + 1

    當(dāng)i=0或者j=0時(shí),f(i,j) = 0 //遞歸出口,邊界情況

    1.2 dp矩陣

    有了遞歸公式就可以設(shè)計(jì)dp表格了,假設(shè)x="caba" , y="bab" , 二維數(shù)組dp[i][j]對(duì)應(yīng)f(i,j)。當(dāng)x[i]==y[j]時(shí),dp[i][j]=dp[i-1][j-1]+1

    從左到右,從上到下計(jì)算得到dp表格:

    同樣的本題的dp表格如下

    1.3 AC代碼

    class LongestSubstring { public:int findLongest(string A, int n, string B, int m) {//初始化n*m matrixvector<vector<int> > dp(n,vector<int>(m,0));int res=0;for(int i=0;i<n;i++)//x串{for(int j=0;j<m;j++)// y串{if(A.at(i)==B.at(j)){if(i==0||j==0) dp[i][j]=1;else dp[i][j]=dp[i-1][j-1]+1;// udpate maximumif(res<dp[i][j]) res=dp[i][j];}}}return res;} };

    2 最長(zhǎng)公共子序列LCS

    問(wèn)題定義

    對(duì)于兩個(gè)字符串,請(qǐng)?jiān)O(shè)計(jì)一個(gè)高效算法,求他們的最長(zhǎng)公共子序列的長(zhǎng)度, 這里的最長(zhǎng)公共子序列定義為:有字符串A的下標(biāo)序列U1,U2,U3...Un和字符串B的下標(biāo)序列V1,V2,V3...Vn,其中Ui < Ui+1,Vi < Vi+1。且A[Ui] == B[Vi]。

    給定兩個(gè)字符串A和B,同時(shí)給定兩個(gè)串的長(zhǎng)度n和m,請(qǐng)返回最長(zhǎng)公共子序列的長(zhǎng)度。保證兩串長(zhǎng)度均小于等于300。

    測(cè)試樣例:"1A2C3D4B56",10,"B1D23CA45B6A",12

    返回:6

    2.1 遞歸公式

    假設(shè)有字符串x和y, 這里我們用x[0,i]來(lái)表示x中下標(biāo)為[0,i]的子串切片,對(duì)應(yīng)于python中的x[0:i+1]。比如x="abcdea",那么x[0,1]就表示"ab"。

    定義f(i,j)表示x[0,i]和y[0,j]之間的最長(zhǎng)公共子序列。

    毫無(wú)疑問(wèn)我們又要想辦法用f(i-1,j-1)來(lái)表示f(i,j)。

    當(dāng)x[i]==y[j]時(shí),f(i,j) = f(i-1,j-1) +1

    當(dāng)x[i]!=y[j]時(shí),f(i,j) = max( f(i-1,j) , f(i,j-1) )

    同樣的為保證f()參數(shù)合法,當(dāng)參數(shù)小于0時(shí),返回值為0。

    當(dāng)i<0或者j<0時(shí),f(i,j)=0 //遞歸出口

    這不難理解,比如f(0,0)實(shí)際上比較的是x[0]和y[0]兩個(gè)字符的最長(zhǎng)公共子序列,無(wú)非是0或者1。

    而f(-1,2)中參數(shù)-1表示x取一個(gè)空串,y取y[0,2]。空串和任何字符串的LCS毫無(wú)疑問(wèn)都是0

    2.2 dp矩陣

    以x="abcdea",y="aebcda"為例,構(gòu)造dp表格如下:

    2.3 AC代碼

    class LCS { public:int findLCS(string A, int n, string B, int m) {vector<vector<int> > dp(n,vector<int>(m,0));for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(A[i]==B[j]){ int t=0;if(i-1>=0 && j-1>=0) t=dp[i-1][j-1];dp[i][j]=t+1;} else{ int t1=0;int t2=0;if(i-1>=0) t1=dp[i-1][j];if(j-1>=0) t2=dp[i][j-1];dp[i][j]=max(t1,t2);}}}//右下角return dp[n-1][m-1];} };

    這里需要考慮的邊界情況較多,為簡(jiǎn)化代碼,可以考慮字符串從1開(kāi)始數(shù)

    dp[i][j]就表示x[0,i-1]和y[0,j-1]的最長(zhǎng)公共子序列

    對(duì)應(yīng)遞歸公式[1]:

    這樣的dp矩陣,字符串的下標(biāo)和矩陣的行號(hào)列號(hào)會(huì)有些錯(cuò)位:

    不需要判斷越界問(wèn)題,代碼精簡(jiǎn),但是不好理解

    class LCS { public:int findLCS(string A, int n, string B, int m) {//(n+1)*(m+1)vector<vector<int> > dp(n+1,vector<int>(m+1,0)); //按照dp的index來(lái)填充矩陣for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){ if(A[i-1]==B[j-1]) dp[i][j]=dp[i-1][j-1]+1;else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}}return dp[n][m];} };//或者class LCS { public:int findLCS(string A, int n, string B, int m) {vector<vector<int> > dp(n+1,vector<int>(m+1,0)); //按照string的索引來(lái)填充dp矩陣for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(A[i]==B[j]) dp[i+1][j+1]=dp[i][j]+1;else dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);}}return dp[n][m];} };

    3 01背包問(wèn)題

    問(wèn)題定義[2]

    話說(shuō)有一哥們?nèi)ド掷锿姘l(fā)現(xiàn)了一堆寶石,他數(shù)了數(shù),一共有n個(gè)。 但他身上能裝寶石的就只有一個(gè)背包,背包的容量為C。這哥們把n個(gè)寶石排成一排并編上號(hào): 0,1,2,…,n-1。第i個(gè)寶石對(duì)應(yīng)的價(jià)值和重量分別為V[i]和W[i] 。排好后這哥們開(kāi)始思考: 背包總共也就只能裝下體積為C的東西,那我要裝下哪些寶石才能讓我獲得最大的利益呢?

    背包問(wèn)題分為01背包問(wèn)題和部分背包問(wèn)題,區(qū)別在于物品是否不可分割。

    這里的物品不能分割,討論的是01背包問(wèn)題。

    假設(shè)有1,2,3...n一共n個(gè)物品。其中v[i]表示第i個(gè)物品價(jià)值。w[i]表示第i個(gè)物品重量。

    d(i,j)表示,要把前i個(gè)物品放入背包,背包容量為j

    • 1 遞歸公式:d(i,j)=

      • 0 //i is 0
      • max( d(i-1,j) , d(i-1,j-w[i])+v[i] ) //j>=w[i] and i>0
    • 2 列表計(jì)算

    占位

    #include<iostream> #include <stdio.h> #include<vector> //#include"fsjtools.h" using namespace std; int main() {int n,c;//number,capwhile(cin>>n>>c){vector<int> w(n+1,0);//weightvector<int> v(n+1,0);//valuefor(int i=1;i<=n;i++) cin>>w[i]>>v[i];//start from 1//printVector(w);//printVector(v);vector<vector<int> > dp(n+1,vector<int>(c+1,0));//初始化為0for(int i=1;i<=n;i++){for(int j=0;j<=c;j++){if(j>=w[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]] + v[i]);else dp[i][j]=dp[i-1][j];}}cout<<dp[n][c]<<endl;} } 樣例輸入5 10 4 9 3 6 5 1 2 4 5 14 9 4 20 3 6 4 20 2 45 10 2 6 2 3 6 5 5 4 4 6樣例輸出19 40 15

    References

  • DP:LCS(最長(zhǎng)公共子串、最長(zhǎng)公共子序列)
  • 動(dòng)態(tài)規(guī)劃之背包問(wèn)題(一)
  • 轉(zhuǎn)載于:https://my.oschina.net/SnifferApache/blog/754122

    總結(jié)

    以上是生活随笔為你收集整理的动态规划套路在最长公共子串、最长公共子序列和01背包问题中的应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。