1、最长公共子序列问题(LCS问题)
给定两个字符串A和B,长度分别为m和n,要求找出它们最长的公共子序列,并返回其长度。例如:
A = “HelloWorld”
B = “loop”
则A与B的最长公共子序列为"loo",返回长度为3。
动态规划解法:
定义子问题:
dp[i][j]为字符串A的第一个字符到第i个字符串和字符串B的的第一个字符到第j个字符的最长公共子序列,如A为“app”,B为“apple”,dp[2][3]表示“ap”和“app”的最长公共子串。注意到代码中dp的大小为(n+1)x(m+1),这多出来的一行和一列是第0行和第0列。
初始条件:
“”和任意字符串的都为0,所以dp[i][0]和dp[0][j]都为0。
状态方程:
代码如下:
int findLCS(string str1, string str2) {
int m = str1.size(), n = str2.size();
vector<vector<int> > dp(m + 1, vector<int>(n + 1));
for (int i = 0; i <= m; i++) {
dp[i][0] = 0;
}
for (int j = 0; j <= n; j++) {
dp[0][j] = 0;
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (str1[i] == str2[j]) {
dp[i][j] = dp[i - 1][j - 1]+1;
}
else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
2、最长公共子串问题
给定两个字符串A和B,长度分别为m和n,要求找出它们最长的公共子串,并返回其长度。例如:
A = “HelloWorld”
B = “loop”
子问题:
定义dp[i][j]表示以A中第i个字符结尾的子串和B中第j个字符结尾的子串的的最大公共子串(公共子串实际上指的是这两个子串的所有部分)的长度(要注意这里和LCS的不同,LCS中的dp[i+1][j+1]一定是大于等于dp[i][j]的;但最长公共子串问题就不一定了,它的dp[i][j]表示的子串不一定是以A[0]开头B[0]开头的,但是一定是以A[i-1]、B[j-1]结尾的),同样地, dp 的大小也为 (n + 1) x (m + 1) ,这多出来的一行和一列是第 0 行和第 0 列,初始化为 0,表示空字符串和另一字符串的子串的最长公共子串。
边界:
当我们要求dp[i][j],我们要先判断A的第i个元素B的第j个元素是否相同即判断A[i - 1]和 B[j -1]是否相同,如果相同它就是dp[i - 1][j- 1] + 1,相当于在两个字符串都去掉一个字符时的最长公共子串再加 1;否则最长公共子串取0。所以整个问题的初始状态为:
状态转移方程:
int findLong(string str1, string str2) {
int m = str1.size(), n = str2.size();
vector<vector<int> > dp(m + 1, vector<int>(n + 1));
for (int i = 0; i <= m; i++) {
dp[i][0] = 0;
}
for (int j = 0; j <= n; j++) {
dp[0][j] = 0;
}
int maxLong = 0;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (str1[i] == str2[j]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else {
dp[i][j] = 0;
}
maxLong = max(maxLong, dp[i][j]);
}
}
return maxLong;
}