【题解】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]分为四种情况:

  1. i和j都不选,即 0 0
  2. i选、j不选,即 1 0
  3. i不选、j选,即 0 1
  4. 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;
}
posted @ 2021-02-20 20:41  Livinfly  阅读(190)  评论(0编辑  收藏  举报