Catching Cheaters(DP)

220 阅读2分钟

Problem - 1446B - Codeforces

原题题面

image.png

题目描述

给你两个字符串 AABB,对于两个字符串 CCDD 我们定义一个函数,S(C,D)=4LCS(C,D)CDS(C,D)=4 \cdot LCS(C,D)-|C|-|D|LCS(C,D)LCS(C,D) 代表 CCDD 的最长公共子序列,X|X| 代表字符串 XX 的长度。

现在,我们设字符串 CCDD 分别为字符串 AABB 的子串,请计算 S(C,D)S(C,D) 的最大值。

输入格式

第一行给出两个数字 nn 和 m (1n,m5000)m (1≤n,m≤5000) 代表 AA 字符串和 BB 字符串的长度。

第二行给出一个长度为 nn 的只包含小写字母的字符串 AA

第三行给出一个长度为 mm 的只包含小写字母的字符串 BB

输出格式

输出 S(C,D)S(C,D) 的最大值,CC 代表 AA 的子串,DD 代表 BB 的子串。

input1

4 5
abba
babab

output1

5

input2

8 10
bbbbabab
bbbabaaaaa

output2

12

题目分析

这是一道 线性DP 的题目。

我们可以巧妙地由求最长公共子序列的思想来思考本题。

定义 f[i][j]f[i][j] 表示字符串 AA 的前 ii 个字符和字符串 BB 的前 jj 个字符中,函数 SS 的最大值。

考虑转移方式,若某一字符串增加一个字符且未产生收益,相当于原 SS 值减一。

f[i][j]f[i][j] 可以由 f[i][j1]1f[i][j-1] - 1 转移而来,也可以由 f[i1][j]1f[i-1][j] - 1 转移而来。

A[i]==B[j]A[i]==B[j],则 f[i][j]f[i][j] 可以由 f[i1][j1]+2f[i-1][j-1]+2 转移而来。

可是题目要求的是某一连续子串,而此解法仅能解答以第一个字符为起点的子串。

为了解决这个问题,我们在每次更新函数 SS 时,需要保证 SS 的最小值大于等于 00。我们假设最优解对应的两个子串分别为字符串 AAal1Ar1a_{l1} - A_{r1} 和字符串 BBbl2br2b_{l2}-b_{r2},那么我们可以推断以 al11a_{l1-1} 和以 bl21b_{l2-1} 为结尾的子串组合的 SS 一定小于 00

若要保证最终答案的正确性,我们只需要将 f[l11][l21]=0f[l_1-1][l_2-1] = 0 即可,为方便最终计算,我们可以将所有 SS 值为负的子串组合的 SS 均取 00

复杂度为 O(nm)O(nm)

Accept代码

#include <bits/stdc++.h>

using namespace std;

const int N = 5010;

int f[N][N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n, m;
    cin >> n >> m;

    string a, b;
    cin >> a >> b;
    a = " " + a, b = " " + b;

    int res = 0;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++)
        {
            f[i][j] = max(f[i][j - 1] - 1, f[i][j]);
            f[i][j] = max(f[i - 1][j] - 1, f[i][j]);
            if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1] + 2;
            res = max(res, f[i][j]);
        }
    cout << res;
    return 0;
}