【题解】AcWing 897. 最长公共子序列
题目传送门
题目描述
给定两个长度分别为N和M的字符串A和B,
求既是A的子序列又是B的子序列的字符串长度最长是多少。
输入格式
第一行包含两个整数N和M。
第二行包含一个长度为N的字符串,表示字符串A。
第三行包含一个长度为M的字符串,表示字符串B。
字符串均由小写字母构成。
输出格式
输出一个整数,表示最大长度。
数据范围
\(1≤N,M≤1000\)
输入样例:
4 5
acbd
abedc
输出样例:
3
算法
(动态规划) \(O(n^2)\)
状态表示
考虑用二维来表示整个集合
f[i][j]表示在第一个字符串s1中的前i个字母
与第二个字符串s2中的前j个字母的最长公共子序列
状态计算
我们可以把f[i][j]分为四种情况:
- i和j都不选,即 0 0
- i选、j不选,即 1 0
- i不选、j选,即 0 1
- i选、j也选,即 1 1
第一种情况 很容易得出f[i][j] = f[i-1][j-1]
第二种情况 用f[i][j] = f[i][j-1]
f[i][j-1] 实际上不一定选择了i,它是选i与不选i两种情况的集合,然而不选i的情况 其实就是第一个情况,它也是f[i][j]的最大值的结果的集合的一个子集,和第三种情况一起重复但是不漏
不影响最大值的值。
因为 求 a, b, c 的最大值 等价于 求 a和b的最大值 与 b和c的最大值 的最大值。
第三种情况 与第二种类似
第四种情况 我个人理解是就是选i和j了
如下面这个例子
abc
acc
最长公共子序列是ac
,我们既可以看成首尾的a和c,也可以看成是ac然后最后一个没用
在这种情况下,如果i与j都选的话,我们看成选s1[i]和s2[j] 和 在i和j前面是否有与它们相同的单个字母的情况是等价的
时间复杂度
两重循环
\(O(n^2)\)
C++ 代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1010;
int len1, len2;
int f[N][N];
char s1[N], s2[N];
int main()
{
cin >> len1 >> len2;
cin >> s1 + 1 >> s2 + 1;
for(int i = 1; i <= len1; i ++)
for(int j = 1; j <= len2; j ++)
{
f[i][j] = max(f[i-1][j], f[i][j-1]);
if(s1[i] == s2[j]) f[i][j] = max(f[i][j], f[i-1][j-1] + 1);
}
cout << f[len1][len2] << endl;
return 0;
}