动态规划——最长公共子序列

Posted 牧空

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划——最长公共子序列相关的知识,希望对你有一定的参考价值。

问题

在字符串S中按照先后顺序依次取出若干字符(不必连续),并将他们排列成一个新的字符串,这个字符串称为原字符串的子串。现在给定两个字符串S1和S2,求一个最长公共子串和其长度。

分析

  • 最基础的方法就是枚举所有子序列,然后逐一判断子序列是否相同,时间复杂度 O ( n n + m ) O(n^{n+m}) O(nn+m)
  • 使用动态规划可将时间复杂度降为 O ( n m ) O(nm) O(nm)
    • 设置二维数组dp[][],令 dp[i][j]表示以S1[i]作为结尾和以S2[j]为结尾的最长子序列的长度,最长为dp[n][m]
    • S1[i]=S2[j],此时必定存在一个最长公共子序列以这个字符为结尾,其他部分等价于S1中前i-1个字符和S2中前j-1个字符中的最长公共子串,也就是dp[i][j]=dp[i-1][j-1]+1
    • S1[i]!=S2[j],则此时最长公共子串为S1中前i-1个字符和S2中前j-1个字符中的较大的最长公共子串,即dp[i][j]=max{dp[i-1][j],dp[i][j-1]}
    • 对于边界条件,如果两个字符串中有一个为空串,那么公共字符串的长度必然为0,即dp[i][0]=dp[0][j]=0

例题

描述
寻找两个字符出的最长子串
输入描述:
输入的两行字符串有小写英文字母构成,且最大长度不会超过100
输出描述:
对于每个例子输出k表示最长公共子串的长度

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#define MAXN 101

using namespace std;

int dp[MAXN][MAXN];
string s1, s2;

int LCS(){
    int n = s1.size(), m = s2.size();
    memset(dp, 0, sizeof(dp));
    // 这里的i,j在dp中表示的所取得子串长度(或者说是所在字符串的第几个字符)
    // 在字符串中要与dp相对应就要-1
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= m; j++){
        	// 上述的两个状态转换
            if (s1[i - 1] == s2[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }
    return dp[n][m];
}

int main(int argc, char const *argv[])
{
    while (cin >> s1 >> s2){
        int k = 0;
        k = LCS();
        printf("%d\\n", k);
    }
    return 0;
}

以上是关于动态规划——最长公共子序列的主要内容,如果未能解决你的问题,请参考以下文章

动态规划 最长公共子序列 过程图解

算法导论—最长公共子序列(动态规划)

最长公共子序列_动态规划

最长公共子序列(LCS)动态规划解题笔记

最长公共子序列(LCS)动态规划解题笔记

动态规划 最长公共子序列