码迷,mamicode.com
首页 > 其他好文 > 详细

最长公共子串

时间:2016-05-12 20:37:18      阅读:135      评论:0      收藏:0      [点我收藏+]

标签:

题目

给定两个字符串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]的值只有两种情况:

  • 如果str1[i] = str2[j],说明str1[i]和str2[j]可以作为公共子串的最后一个字符,所以dp[i][j] = dp[i-1][j-1] + 1;
  • 否则,说明要把str1[i]和str2[j]可以作为公共子串的最后一个字符是不可能的,所以dp[i][j] = 0;


方法二:

经典动态规划方法需要大小为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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!