标签:
给定两个字符串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