(一)最长公共子序列
对于两个字符串,请设计一个高效算法,求他们的最长公共子序列的长度,这里的最长公共子序列定义为有两个序列U1,U2,U3...Un和V1,V2,V3...Vn,其中Ui<Ui+1,Vi<Vi+1。且A[Ui] == B[Vi]。
给定两个字符串A和B,同时给定两个串的长度n和m,请返回最长公共子序列的长度。保证两串长度均小于等于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串所需要的最少代价。
给定两个字符串A和B,及它们的长度和三种操作代价,请返回将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]; } };