• 【题解】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;
    }
    
  • 相关阅读:
    WPF 中英文切换
    System.Data.Entity.Core.ProviderIncompatibleException:0x89c50107
    WinForm使用原生gdi+绘制自定义曲线图、折线图
    C#使用EPPlus读写excel
    ICSharpCode.SharpZipLib C# 压缩文件夹SharpZipHelper
    C# XSLT 转换word 生成word
    代码生成器集合
    优秀的个人博客
    面试经典复习资料
    图解算法
  • 原文地址:https://www.cnblogs.com/RemnantDreammm/p/14423226.html
Copyright © 2020-2023  润新知