动态规划-最长公共子序列长度/最长公共子串/最小编辑代价

(一)最长公共子序列

 

对于两个字符串,请设计一个高效算法,求他们的最长公共子序列的长度,这里的最长公共子序列定义为有两个序列U1,U2,U3...Un和V1,V2,V3...Vn,其中Ui&ltUi+1,Vi&ltVi+1。且A[Ui] == B[Vi]。

给定两个字符串AB,同时给定两个串的长度nm,请返回最长公共子序列的长度。保证两串长度均小于等于300。(子序列可以是不连续的)

测试样例:
"1A2C3D4B56",10,"B1D23CA45B6A",12
返回:6

1)如果 xn=ym,即X的最后一个元素与Y的最后一个元素相同,这说明该元素一定位于公共子序列中。因此,现在只需要找:LCS(Xn-1,Ym-1)

LCS(Xn-1,Ym-1)就是原问题的一个子问题。为什么叫子问题?因为它的规模比原问题小。(小一个元素也是小嘛....)

为什么是最优的子问题?因为我们要找的是Xn-1 和 Ym-1 的最长公共子序列啊。。。最长的!!!换句话说,就是最优的那个。(这里的最优就是最长的意思)

2)如果xn != ym,这下要麻烦一点,因为它产生了两个子问题:LCS(Xn-1,Ym) 和 LCS(Xn,Ym-1)

因为序列X 和 序列Y 的最后一个元素不相等嘛,那说明最后一个元素不可能是最长公共子序列中的元素嘛。(都不相等了,怎么公共嘛)。

LCS(Xn-1,Ym)表示:最长公共序列可以在(x1,x2,....x(n-1)) 和 (y1,y2,...yn)中找。

LCS(Xn,Ym-1)表示:最长公共序列可以在(x1,x2,....xn) 和 (y1,y2,...y(n-1))中找。

求解上面两个子问题,得到的公共子序列谁最长,那谁就是 LCS(X,Y)。用数学表示就是:

LCS=max{LCS(Xn-1,Ym),LCS(Xn,Ym-1)}

状态转移方程为:

建立一个(n+1)(m+1)维数组存放公共子序列的长度。

class LCS {
public:
    int findLCS(string A, int n, string B, int m) {
        // write code here
        int dp[301][301];
        for(int i=0;i<=n;++i){
            dp[i][0]=0;
        }
        for(int j=0;j<=m;++j){
            dp[0][j]=0;
        }
        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];
        
    }
};

 (二)最长公共子串

上面最长公共子序列要求是不连续的,而最长公共子串要求是连续的,差别就在状态方程的变化:

当A[i]==B[j],dp[i][j]=dp[i-1][j-1]+1;当A[i]!=B[i]时,dp[i][j]=0

class LongestSubstring {
public:
    int findLongest(string A, int n, string B, int m) {
        // write code here
        int maxLen=0;
        int dp[301][301];
        for(int i=0;i<=n;++i){
            dp[i][0]=0;
        }
        for(int j=0;j<=m;++j){
            dp[0][j]=0;
        }
        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] = 0;
                }
                maxLen = max(maxLen, dp[i][j]);
            }
        }
         return maxLen;
    }
   
};

 (三)最小编辑代价

链接:https://www.nowcoder.com/questionTerminal/04f1731f32e246b4a19688972d5e2600 来源:牛客网

对于两个字符串A和B,我们需要进行插入、删除和修改操作将A串变为B串,定义c0,c1,c2分别为三种操作的代价,请设计一个高效算法,求出将A串变为B串所需要的最少代价。

给定两个字符串AB,及它们的长度和三种操作代价,请返回将A串变为B串所需要的最小代价。保证两串长度均小于等于300,且三种代价值均小于等于100。

测试样例:
"abc",3,"adc",3,5,3,100
返回:8

思路与之前类似,也是建立一个(n+1)(m+1)辅助数组,因为要把A字符串变为B字符串,当j=0时(说明B字符串为空),每一行的代价为删除代价,代价为i*c1;当i=0时,每一列的代价为插入代价,代价为j*c0。

1)修改=删除+插入 c2=min(c2,c0+c1)

2)当两个数相等时dp[i][j]=dp[i-1][j-1]

2)当两个数不相等时,有三种不同情况dp[i-1][j]+c1, dp[i][j-1]+c0, dp[i-1][j-1]+c2;

代码如下:

class MinCost {
public:
    int findMinCost(string A, int n, string B, int m, int c0, int c1, int c2) {
        // write code here
        int dp[301][301];
        for(int i=0;i<=n;++i){
            dp[i][0]=i*c1;
        }
        for(int j=0;j<=m;++j){
            dp[0][j]=j*c0;
        }
        c2=min(c2,c0+c1);
        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];
                }
                else{
                    dp[i][j]=min(dp[i-1][j-1]+c2,min(dp[i-1][j]+c1,dp[i][j-1]+c0));
                }
            }
        }
        return dp[n][m];
    }
};
原文地址:https://www.cnblogs.com/rgly/p/7573806.html