算法设计与分析 SCAU11077 最长公共子字符串(优先做)

11077 最长公共子字符串(优先做)

时间限制:1000MS 代码长度限制:10KB
提交次数:0 通过次数:0

题型: 编程题 语言: G++;GCC;VC;JAVA

Description

求两个输入序列的最长的公共子字符串的长度。子字符串中的所有字符在源字符串中必须相邻。

如字符串:21232523311324和字符串312123223445,他们的最长公共子字符串为21232,长度为5。


输入格式

两行,第一行为第一个字符串X,第二行为第二个字符串Y,字符串不含空格并以回车标示结束。X和Y的串长都
不超过10000。

输出格式

两行,第一行为最长的公共子字符串的长度,第二行输出一个最长的公共子字符串。

说明:
(1)若最长的公共子字符串有多个,请输出在源字符串X中靠左的那个。
(2)若最长公共子字符串的长度为0,请输出空串(一个换行符)。

如输入:
21232523311324
152341231
由于523和123都是最长的公共子字符串,但123在源串X中更靠左,因此输出:
3
123


解题思路

此题思路类似:算法设计与分析 SCAU11084 多个串的最长公共子序列

动态规划

以下解释中

  • text1[0, i] 代表字符串 text1 的前 i + 1 个字符
  • text2[0, j] 代表字符串 text2 的前 j + 1 个字符

状态定义:dp[i][j] 的值代表 text1 字符串前 i 个字符和 text2 字符串前 j 个字符的最长公共子字符串的长度。

假设字符串 text1 和 text2 的长度分别为 m 和 n,创建 m+1 行 n+1 列的二维数组 dp,其中 dp[i][j] 表示 text1[0, i] 和 text2[0, j] 的最长公共子字符串的长度。

例如:dp[3][4] 代表 text1 字符串前三个字符和 text2 字符串前四个字符的最长公共子字符串的长度。

注意
  1. 这题求的是最长公共子串,不是最长公共子序列,子序列可以是不连续的,但子串一定是连续的。

  2. 定义 dp[i][j] 表示字符串 str1 中第 i 个字符和 str2 种第 j 个字符为最后一个元素所构成的最长公共子串。如果要求 dp[i][j],也就是 str1 的第 i 个字符和 str2 的第 j 个字符为最后一个元素所构成的最长公共子串,我们首先需要判断这两个字符是否相等。

  3. 如果不相等,那么他们就不能构成公共子串,也就是
    dp[i][j] = 0;

  4. 如果相等,我们还需要计算前面相等字符的个数,其实就是 dp[i - 1][j - 1],所以 dp[i][j] = dp[i - 1][j - 1] + 1;


复杂度分析:
  • 时间复杂度:O(mn),其中 m 和 n 分别是字符串 text1 和 text2 的长度。二维数组 dp 有 m + 1 行和 n + 1 列,需要对 dp 中的每个元素进行计算。
  • 空间复杂度:O(mn),其中 m 和 n 分别是字符串 text 1和 text 2的长度。创建了 m + 1 行 n + 1 列的二维数组 dp。

更多注释可查看代码中,有助于理解

代码如下
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <cstring>
#include <string>

using namespace std;

int maxLength = 0;
int maxEndIndex = 0;

void maxCommonLength(string a, string b) {
    int m = a.length(), n = b.length();
    int dp[m + 1][n + 1];

    for(int i = 0; i <= m; i++) {
        for(int j = 0; j <= n; j++) {
            // 长度为0,公共肯定是0
            if(i == 0 || j == 0) {
                dp[i][j] = 0;
            } else {
                // 如果i索引的m串 和 j索引的n串字符相同,那就+1,同一行的前一个,因为是一行一行遍历的
                if(a[i - 1] == b[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    if(dp[i][j] > maxLength) {
                        maxLength = dp[i][j];
                        maxEndIndex = i - 1;
                    }
                } else {
                    // 由于得连续,所以直接置为0,而不是拿上次的状态
                    dp[i][j] = 0;
                }
                //printf("i=%d j=%d,dp=%d\n", i, j, dp[i][j]);
            }
        }
    }

}

int main()
{
    string s1, s2;
    cin >> s1 >> s2;

    maxCommonLength(s1, s2);

    cout << maxLength << endl;
    cout << s1.substr(maxEndIndex - maxLength + 1, maxLength) << endl;

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值