最长公共子序列方案

📘 题目描述

给定两个字符串 $s,t$,构造任意一组 LCS 方案。字符串长度 $1\leq |s|,|t|\leq 2000$。

💡 解题思路

🔢 状态定义

  • $f_{i,j}$ 表示字符串 $s[1..i]$ 和字符串 $t[1..j]$ 的最长公共子序列长度。
  • $g_{i,j}$ 记录状态 $i,j$ 的转移来源是什么。
    • 若 $f_{i,j}$ 的最大值由 $f_{i-1,j-1}+1$ 得到,则 $g_{i,j} = 1$。
    • 若 $f_{i,j}$ 的最大值由 $f_{i-1,j}$ 获得,则 $g_{i,j} = 2$。
    • 若 $f_{i,j}$ 的最大值由 $f_{i,j-1}$ 获得,则 $g_{i,j} = 3$。

最后递归回溯输出方案即可。

⏱️复杂度分析

  • 时间复杂度:$O(n\cdot m)$。
  • 空间复杂度:$O(n\cdot m)$。

✅ 代码实现

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ary = array<int, 2>;
template <typename T>
using vec = vector<T>;
constexpr int N = 3e3 + 5, mod = 998244353;
int f[N][N], g[N][N];
string a, b;
void print(int i, int j)
{
    if (!i || !j) return ;
    if (g[i][j] == 3) 
    {
        print(i - 1, j - 1);
        cout << a[i];
    }
    else if (g[i][j] == 1) print(i - 1, j);
    else print(i, j - 1);
}
int main()
{
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> a >> b;
    int n = a.size(), m = b.size();
    a = " " + a, b = " " + b;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (a[i] == b[j])
            {
                if (f[i - 1][j - 1] + 1 > f[i][j])
                {
                    f[i][j] = f[i - 1][j - 1] + 1;
                    g[i][j] = 3;
                }
            }
            else
            {
                if (f[i - 1][j] > f[i][j - 1])
                {
                    f[i][j] = f[i - 1][j];
                    g[i][j] = 1;
                }
                else
                {
                    f[i][j] = f[i][j - 1];
                    g[i][j] = 2;
                }
            }
        }
    }
    print(n, m);

    return 0; 
}