[动态规划] 最长公共子串问题

  算法专题导航页面


【题目描述】
    给定两个字符串str1和str2,输出两个字符串的最长公共子串,如果最长公共子串为空,输出-1。

【输入描述】
    输入包括两行,第一行代表字符串srr1,第二行代表字符串str2。(1≤length(str1),length(str2)≤5000)

【输出描述】
    输出包括一行,代表最长公共子串。


【示例1】
    输入
        1AB2345CD
        12345EF
    输出
        2345

【备注】
    时间复杂度O(n^2),额外空间复杂度O(1)。(n可以为其中任意一个字符串长度)


【代码实现 - CPP版 - 经典动态规划】

#include<iostream>
#include<string>
#include<vector>

using namespace std;


/*
 * dp[i][j]表示以stra[i]和strb[j]结尾的字符串的长度
 * 第一种情况:stra[i] == strb[j] 公共字符串存在,则有递推公式: dp[i][j] = dp[i-1][j-1] + 1
 * 第二种情况:stra[i] != strb[j] 公共字符串不存在,则dp[i][j] = 0
 */

void dp_generator(const string& stra, const string& strb, vector<vector<int>>& dp) {
    int lena = stra.length();
    int lenb = strb.length();
    
    for(int i=0; i<lena; i++) {
        if(stra[i] == strb[0]) {
            dp[i][0] = 1;
        }
    }
    
    for(int j=0; j<lenb; j++) {
        if(stra[0] == strb[j]) {
            dp[0][j] = 1;
        }
    }
    
    for(int i=1; i<lena; i++) {
        for(int j=1; j<lenb; j++) {
            if (stra[i] == strb[j]) {
                dp[i][j] = dp[i-1][j-1] + 1; 
            }
        }
    }
}

void position_and_maxlen(int& endindex, int& maxlen, const vector<vector<int>>& dp) {
    int row = dp.size();
    int col = dp[0].size();
    
    for(int i=0; i<row; i++) {
        for(int j=0; j<col; j++) {
            if(dp[i][j] > maxlen) {
                endindex = i;
                maxlen = dp[i][j];
            }
        }
    }
}


int main() {
    string str_a;
    string str_b;
    int lena = 0;
    int lenb = 0;
    
    cin >> str_a;
    cin >> str_b;

    lena = str_a.length();
    lenb = str_b.length();

    // 边界条件
    if (lena <=0 || lenb <=0)
        return -1;
    
    // 定义并初始化动态规划二维数组
    vector<vector<int>> dp(lena, vector<int>(lenb, 0));
    dp_generator(str_a, str_b, dp);
    
    // 遍历动态规划数组,获取最大元素
    int max_len = 0;
    int end_index = 0;
    position_and_maxlen(end_index, max_len, dp);
    
    // 最大元素可能为0,反之截取字符串并返回
    if (0 == max_len)
        cout << -1;
    else
        cout << str_a.substr(end_index - max_len +1, max_len) << endl;
    
    return 0;
}

【代码实现 - CPP版 - 空间压缩法】

#include<iostream>
#include<string>

using namespace std;


void position_and_maxlen_2(const string& stra, const string& strb, int& maxlen, int& endindex) {
    int len_a = stra.size();
    int len_b = strb.size();
    
    // 斜线遍历策略的起始行列
    int row = 0;
    int col = len_b - 1;
    // 最大长度以及对应的字符串结尾索引值
    maxlen = 0;
    endindex = 0;
    
    while (row < len_a) {
        int i = row;
        int j = col;
        int len = 0;
        // 遍历以[i,j]为左上起点到右下的斜线
        while(i < len_a && j < len_b) {
            if(stra[i] != strb[j]) {
                len = 0;
            } else {
                len++;
            }
            // 记录当前斜线上的最大值,以及结束字符的位置
            if(len > maxlen) {
                endindex = i;
                maxlen = len;
            }
            // 位置[i,j]计算完成,继续想右下角遍历
            i++;
            j++;
        }
        
        // 当前斜线处理完毕,遍历其左侧斜线
        if(col > 0) {
            col--;
        // 已经完成矩阵对角线斜线的遍历,此时列索引为零。需要向右下方移动,计算新的斜线上所有元素的值
        } else {
            row++;
        }
    }
}


int main() {
    string str_a;
    string str_b;
    int lena = 0;
    int lenb = 0;
    
    cin >> str_a;
    cin >> str_b;

    lena = str_a.length();
    lenb = str_b.length();

    // 边界条件
    if (lena <=0 || lenb <=0)
        return -1;
    
    int max_len = 0;
    int end_index = 0;
    
    position_and_maxlen_2(str_a, str_b, max_len, end_index);
    
    // 最大元素可能为0,反之截取字符串并返回
    if (0 == max_len)
        cout << -1; // 请勿忽略此类情况
    else
        cout << str_a.substr(end_index - max_len +1, max_len) << endl;

    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值