标签:
给定两个字符串str1和str2,返回两个字符串的最长公共子串。
举例:str1=“1AB2345CD”,str2="12345EF",返回“2345”
典型的动态规划问题。假设str1的长度为m,str2的长度为n。
方法一:时间复杂度O(mn),空间复杂度O(mn)
声明大小为m*n的矩阵dp,求解二维动态规划表,行数为m,列数为n。dp[i][j]的含义是必须把str1[i]和str2[j]当作公共子串最后一个字符的情况下,公共子串的最大长度。dp[i][j]计算如下:
1. 矩阵dp第一列,dp[0...m][0],对某一个位置(i,0)来说,如果str1[i] = str2[0],令dp[i][0]=1,否则dp[i][0]=0;
2. 同理,矩阵dp第一行,dp[0][0...n],对某一个位置(0,j)来说,如果str1[0] = str2[j],令dp[0][j]=1,否则dp[0][j]=0;
3. 其他位置按照从左向右,从上到下来计算,dp[i][j]的值只有两种情况:
方法二:
经典动态规划方法需要大小为m*n的矩阵,实际上可以优化为O(1),因为我们注意到,计算每一个dp[i][j]时,最多只需要其左上方dp[i-1][j-1]的值,所以按照斜线方向计算所有的值,只需要一个变量就可以计算出所有位置的值。
/* 4.8 最长公共子串 给定两个字符串str1和str2,返回两个字符串的最长公共子串。 举例:str1=“1AB2345CD”,str2="12345EF",返回“2345” */ #include <iostream> #include <cstdlib> #include <vector> #include <string> #include <algorithm> using namespace std; /*计算动态规划矩阵*/ vector<vector<int>> getDp(string A, string B) { // write your code here if (A.empty() || B.empty()) { return vector<vector<int>>(); }//if int aLen = A.size(), bLen = B.size(); /*动态规划,时间复杂度O(mn) 空间复杂度O(mn) dp[i][j]表示必须把A[i]和B[j]作为公共子串的最后一个字符时的子串最大长度*/ vector<vector<int>> dp(aLen, vector<int>(bLen, 0)); dp[0][0] = A[0] == B[0] ? 1 : 0; for (int i = 1; i<aLen; ++i) { dp[i][0] = A[i] == B[0] ? 1 : 0; }//for for (int j = 1; j<bLen; ++j) { dp[0][j] = A[0] == B[j] ? 1 : 0; }//for for (int i = 1; i<aLen; ++i) { for (int j = 1; j<bLen; ++j) { if (A[i] == B[j]) { dp[i][j] = dp[i - 1][j - 1] + 1; }//if else { dp[i][j] = 0; }//if }//for }//for /*此时最长公共子串的长度即为矩阵dp中的最大值*/ return dp; } /*方法一:经典动态规划 时间复杂度O(m*n) 空间复杂度O(m*n)*/ string longestCommonSubstring(string str1, string str2) { if (str1.empty() || str2.empty()) { return ""; }//if /*得到最长公共子串长度的动态规划长度矩阵*/ vector<vector<int>> dp = getDp(str1, str2); int m = str1.length(), n = str2.length(), maxLen = 0, end = 0; /*找到最长公共子串的长度,和其在str1中的结束位置*/ for(int i = 0 ; i < m ; ++i) { for (int j = 0; j < n; ++j) { if (dp[i][j] > maxLen) { maxLen = dp[i][j]; end = i; }//if }//for }//for return str1.substr(end - maxLen + 1, maxLen); } /*方法二:优化算法 空间复杂度O(1)*/ string longestCommonSubstring2(string str1, string str2) { if (str1.empty() || str2.empty()) { return ""; }//if /*斜线开始的行和列*/ int row = 0, col = str2.length() - 1; /*公共子串最大长度,和str1中的结束位置*/ int maxLen = 0, end = 0; while (row < str1.length()) { int i = row, j = col, len = 0; /*从(i,j)开始向右下方遍历*/ while (i < str1.length() && j < str2.length()) { if (str1[i] != str2[j]) { len = 0; }//if else { ++len; }//else /*记录最大值以及结束字符的位置*/ if (maxLen < len) { maxLen = len; end = i; }//if ++i; ++j; }//while /*斜线开始位置的列先向左移动*/ if (col > 0) { --col; }//if /*列移动到最左以后,行向下移动*/ else { ++row; }//else }//while return str1.substr(end - maxLen + 1, maxLen); } int main() { string str1 = "1AB2345CD", str2 = "12345EF"; cout << longestCommonSubstring2(str1, str2) << endl; system("pause"); return 0; }GitHub代码
标签:
原文地址:http://blog.csdn.net/fly_yr/article/details/51356484