#dp#C 公共子序列

题目

给定两个字符串\(s1,s2\),求它们的\(LCS\)
满足\(|s1|\leq 10^6,|s2|\leq 10^3\)


分析

考场写了\(O(|s1|*|s2|)\)成功TLE,
考虑突破口为\(|s2|\)不够大,考虑转为判定,
\(dp[i][j]\)表示原来存在最小的\(k\)使得\(f[k][i]\geq j\),不存在为\(n+1\)
那么\(dp[i][j]=\min\{dp[i-1][j],nxt[dp[i-1][j-1]][s2[i]]\}\)
然后二分\(dp[m][ans]\)即可,\(nxt\)数组要预处理,其实就是子序列自动机
转为判定是真的妙


代码

#include <cstdio>
#include <cstring>
#define rr register
using namespace std;
const int N = 1011, M = 1000011;
char s1[M], s2[N];
int dp[N][N], nxt[M][26], ls[26], n, m;
inline signed min(int a, int b) { return a < b ? a : b; }
signed main() {
    freopen("lcs.in", "r", stdin);
    freopen("lcs.out", "w", stdout);
    scanf("%s%s", s1 + 1, s2 + 1), memset(dp, 42, sizeof(dp));
    n = strlen(s1 + 1), m = strlen(s2 + 1);
    memset(ls, 42, sizeof(ls));
    for (rr int i = 0; i <= m; ++i) dp[i][0] = 0;
    for (rr int i = n; i >= 0; --i) {
        for (rr int j = 0; j < 26; ++j) nxt[i][j] = ls[j];
        if (i > 0)
            ls[s1[i] - 97] = i;
    }
    for (rr int i = 1; i <= m; ++i)
        for (rr int j = 1; j <= i; ++j) {
            dp[i][j] = dp[i - 1][j];
            if (dp[i - 1][j - 1] <= n)
                dp[i][j] = min(dp[i][j], nxt[dp[i - 1][j - 1]][s2[i] - 97]);
        }
    rr int l = 0, r = m;
    while (l < r) {
        rr int mid = (l + r + 1) >> 1;
        if (dp[m][mid] <= n)
            l = mid;
        else
            r = mid - 1;
    }
    return !printf("%d", l);
}
posted @ 2020-12-01 14:29  lemondinosaur  阅读(72)  评论(0编辑  收藏  举报