[DP]最长公共子串

题目

给定两个字符串str1和str2, 长度分别稳M和N,返回两个字符串的最长公共子串

解法一

这是一道经典的动态规划题,可以用M*N的二维dp数组求解.dp[i][j]代表以str1[i]和str2[j]当做公共子串结尾的情况下,公共子串能有多长.然后分析可以发现dp[i][j]只与dp[i- 1][j - 1]有关:当str1[i] 和str2[j]相等的时候, dp[i][j] = dp[i - 1][j - 1] + 1;当它们不相等的时候,dp[i][j] = 0.获得dp数组之后要获得最长公共子串只需要找到数组中最大的值,其值代表最长子串的长度,其所在的索引代表了子串在原字符串中结束的位置.

代码

先获取dp数组,然后通过dp数组得到最长公共子串

//最长公共子串的dp数组
vector<vector<int> > getdp1(string str1, string str2) {
    vector<vector<int> > dp(str1.length(), vector<int>(str2.length()));
    if (str1.length() == 0 || str2.length() == 0)
        return dp;
    dp[0][0] = str1[0] == str2[0] ? 1 : 0;
    for (int i = 1; i < int(str1.length()); i ++)
        dp[i][0] = str1[i] == str2[0] ? 1 : 0;
    for (int j = 1; j < int(str2.length()); j ++)
        dp[0][j] = str2[j] == str1[0] ? 1 : 0;

    for (int i = 1; i < int(str1.length()); i ++) {
        for (int j = 1; j < int(str2.length()); j ++) {
            if (str1[i] == str2[j])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = 0;
        }
    }
    return dp;
}

// 最长公共子串的解法一
string lcst1(string str1, string str2) {
    string res = "";
    if (int(str1.length()) == 0 || int(str2.length()) == 0)
        return res;
    vector<vector<int> > dp = getdp1(str1, str2);
    int maxNum = 0;
    int maxIndex;
    for (int i = 0; i < int(dp.size()); i ++) { // 遍历dp矩阵找到最大值
        for (int j = 0; j < int(dp[0].size()); j ++)
            if (dp[i][j] > maxNum) {
                maxNum = dp[i][j];
                maxIndex = i;
            }
    }

    res = str1.substr(maxIndex - maxNum + 1, maxNum);
    return res;
}

解法二

上面这种解法需要的时间复杂度和空间复杂度都是O(M* N).下面提供一种空间复杂度为O(1)的解法

代码

//最长公共子串解法二
string lcst2(string str1, string str2) {
    string res = "";
    if (int(str1.length()) == 0 || int(str2.length()) == 0)
        return res;

    int maxNum = 0; //记录最大值
    int maxindex = -1; //记录最大值的索引
    int tmpNum; //记录当前值,如果大于最大值就更新
    int i = int(str1.length()) - 1;
    int j = int(str2.length()) - 1;
    for (j; j >= 0; j --) { // 斜线先向左移动
        tmpNum = 0;
        for (int tmp = 0; tmp < int(str2.length()) - j; tmp ++){
            if (str1[tmp] == str2[j + tmp]) {
                tmpNum = tmpNum == 0 ? 1 : tmpNum + 1;
            } else
                tmpNum = 0;
            if (tmpNum > maxNum) {
                maxNum = tmpNum;
                maxindex = j + tmp;
            }
        }
    }

    bool triangleUpFlag = true; //maxindex 在通过斜线计算的时候属于上三角的flag
    for (i; i > 0; i --) { //列移到最左边之后再向下移动
        tmpNum = 0;
        for (int tmp = 0; tmp < int(str1.length()) - i; tmp ++) {
            if (str2[tmp] == str1[i + tmp])
                tmpNum = tmpNum == 0 ? 1 : tmpNum + 1;
            else
                tmpNum == 0;
            if (tmpNum > maxNum) {
                maxNum = tmpNum;
                triangleUpFlag = false;
                maxindex = i + tmp;
            }
        }
    }

    cout<<maxindex<<" "<<maxNum<<" "<<triangleUpFlag<<endl;
    res = triangleUpFlag == true ? str2.substr(maxindex - maxNum + 1, maxNum) : str1.substr(maxindex - maxNum + 1, maxNum);
    return res;
}

int main()
{
    string str1 = "1AB2345CD";
    string str2 = "12345EF";
    //string str1 = "abcde";
    //string str2 = "bebcd";
    cout<<lcst2(str1, str2)<<endl; //输出2345
    return 0;
}
原文地址:https://www.cnblogs.com/mooba/p/7473553.html