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

本文介绍了一种求解两个字符串最长公共子串的问题,并给出了详细的算法思路及两种实现代码。通过递推公式,文章展示了如何利用动态规划高效地解决这一常见问题。

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

题目

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

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

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

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

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

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

输入样例
21232523311324
312123223445

输出样例
5
21232

提示

一,对输入字符串的处理
大家在接受数据的时候,不要用(c=getchar())!=‘\n’诸如此类一个字符一个字符接受,然后判断是否是回车
符号来结束一行的输入,这样的方式在你本机运行不会有问题,但OJ系统中会有错误,无法输出结果,因为
测试平台行末并非’\n’字符。这里接受数据用scanf的%s,或cin等,会自动判别结束字符的,你就不要在你
程序里专门去判别或吸收回车字符。

二,递推公式
此题和书上3.3节"最长公共子序列"描述是不同的,子序列可由不连续字符组成,但子字符串是连续的。
此题更加简单!

假设求字符串str1,str2的最长公共子串的长度.
定义f(m,n): 分别以str1[m],str2[n]结尾的最长连续公共子串的长度,
其中字符串末尾的str1[m]和str2[n]包含在最长公共子串中,即为最长公共子串的最末元素。

(这里大家思考一下,为何要这样假设子问题和子问题最优解f(m,n)?
因为子串是连续的,更大规模问题和下一级更小规模的子问题要能联系起来,而且这种联系还要越简单越好,
只有规定原先两个串的最末元素包含在最长公共子串中,这样就能联系上两个串的前缀部分(都去掉末个元
素)的最长公共子串问题。)

而对于f(m+1,n+1) 有:

  1. f(m+1,n+1) = 0, if str1[m+1] != str2[n+1]
  2. f(m+1,n+1) = f(m,n) + 1, if str1[m+1] == str2[n+1]
  3. 另外边界情况,f(0,j)=0(j>=0), f(j,0)=0(j>=0)

而此题所求的最长公共字符串的长度即为f(m,n)整个二维数组中的最大值,注意不是填充的最后一个元素。
也不是最后一行元素的最大值,而是整个二位数组的最大值。思考一下为什么呢?

至于如何优先输出在源串X靠左的公共子串,大家自行思考。
这也容易,在你比较产生数组最大值maxlen时,就同步记录下那时在x数组中的下标位置,比如此位置叫endindex_x。
最后用这个位置在X序列中输出从X[endindex_x - maxlen + 1]元素一直到X[endindex_x]元素即可。

题解

牛客题解

代码


#include <iostream>
#include <cstring>

using namespace std;
const int N = 10010;
char s1[N], s2[N];
int dp[N][N];

int main()
{
    scanf("%s%s", s1+1, s2+1);
    int len1 = strlen(s1+1), len2 = strlen(s2+1);

    int maxLen = 0, idx = 1;
    for(int i = 1; i <= len1; i++)
        for(int j = 1; j <= len2; j++)
        if(s1[i] == s2[j]){//如果两个字符相等,那么最大公共字串的长度=dp[i-1][j-1] + 1,否则=0
            dp[i][j] = dp[i-1][j-1] +1;
            if(maxLen < dp[i][j]) idx = i, maxLen = dp[i][j];
        }else dp[i][j] = 0;
    cout << maxLen << endl;

    s1[idx+1] = '\0';
    printf("%s\n", s1 + idx-maxLen+1);

    return 0;
}

代码二



#include <iostream>
#include <vector>
using namespace std;

int main()
{
    string s1, s2;
    cin >> s1 >> s2;
    vector<vector<int>>  dp(s1.size() + 1, vector<int>(s2.size()+1));

    int maxLen = 0, idx = 0;
    for(int i = 0; i < s1.size(); i++)
        for(int j = 0; j < s2.size(); j++)
        if(s1[i] == s2[j]){
            dp[i+1][j+1] = dp[i][j] + 1;
            if(maxLen < dp[i+1][j+1]) idx = i, maxLen = dp[i+1][j+1];
        }else dp[i+1][j+1] = 0;
    cout << maxLen << endl;
    cout << s1.substr(idx-maxLen+1, maxLen) << endl;

    return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值