原题题面
题目描述
给你两个字符串 和 ,对于两个字符串 , 我们定义一个函数,, 代表 和 的最长公共子序列, 代表字符串 的长度。
现在,我们设字符串 和 分别为字符串 和 的子串,请计算 的最大值。
输入格式
第一行给出两个数字 和 代表 字符串和 字符串的长度。
第二行给出一个长度为 的只包含小写字母的字符串 。
第三行给出一个长度为 的只包含小写字母的字符串 。
输出格式
输出 的最大值, 代表 的子串, 代表 的子串。
input1
4 5
abba
babab
output1
5
input2
8 10
bbbbabab
bbbabaaaaa
output2
12
题目分析
这是一道 线性DP 的题目。
我们可以巧妙地由求最长公共子序列的思想来思考本题。
定义 表示字符串 的前 个字符和字符串 的前 个字符中,函数 的最大值。
考虑转移方式,若某一字符串增加一个字符且未产生收益,相当于原 值减一。
则 可以由 转移而来,也可以由 转移而来。
若 ,则 可以由 转移而来。
可是题目要求的是某一连续子串,而此解法仅能解答以第一个字符为起点的子串。
为了解决这个问题,我们在每次更新函数 时,需要保证 的最小值大于等于 。我们假设最优解对应的两个子串分别为字符串 的 和字符串 的 ,那么我们可以推断以 和以 为结尾的子串组合的 一定小于 。
若要保证最终答案的正确性,我们只需要将 即可,为方便最终计算,我们可以将所有 值为负的子串组合的 均取 。
复杂度为 。
Accept代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5010;
int f[N][N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
string a, b;
cin >> a >> b;
a = " " + a, b = " " + b;
int res = 0;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
{
f[i][j] = max(f[i][j - 1] - 1, f[i][j]);
f[i][j] = max(f[i - 1][j] - 1, f[i][j]);
if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1] + 2;
res = max(res, f[i][j]);
}
cout << res;
return 0;
}