cf346 B. Lucky Common Subsequence

题意:

输出串 a 和串 b 的,不含串 c 作为子串的最长公共子列。

串长 100

思路:

\(f(i,j,k)\) 表示从 \(a[1\sim i]\)\(b[1\sim j]\) 中选,末端匹配到串 c 的第 \(k\) 个位置(即含 \(c[1\sim k]\) 作为后缀,\(k\) 最大)的最长公共子列。

dp 做 LCS 的过程中,\(a_i\neq b_j\) 时正常更新,即 \(f(i,j)=\max (f(i-1,j),f(i,j-1))\)

\(a_i\neq b_j\) 时,考虑往 \(f(i-1,j-1,k)\) 的末尾添个 \(a_i\) 会变成啥状态。这是一个 kmp 过程,不断 k=ne[k] 往前找到一个能扩展的 c 前缀 \(t-1\),那就可以用 \(f(i-1,j-1,k)\) 更新 \(f(i,j,t)\)

要输出子列,懒得写回溯,全用 string 了

某人为了让string的下标从1开始而疯狂在串首加东西的的样子真狼狈啊。输出答案时记得去掉

const signed N = 103;
int na, nb, nc, ne[N];
string a, b, c, f[N][N][N], ans;

void upd(string &a, string b) {
    if(a.size() < b.size()) a = b;
}

signed main() {
    iofast;
    cin >> a >> b >> c;
    na = a.size(), nb = b.size(), nc = c.size();

    fill_n(f[0][0], N*N*N, "0"); //奇怪的初始化
    a = "0" + a; b = "0" + b; c = "0" + c;

    //getNext
    for(int i = 2, j = 0; i <= nc; i++) {
        while(j && c[i] != c[j+1]) j = ne[j];
        if(c[i] == c[j+1]) j++;
        ne[i] = j;
    }

    for(int i = 1; i <= na; i++)
    for(int j = 1; j <= nb; j++)
        for(int k = 0; k < nc; k++) {
            if(a[i] == b[j]) {
                int t = k;
                while(t && a[i] != c[t+1]) t = ne[t];
                if(a[i] == c[t+1]) t++;
                upd(f[i][j][t], f[i-1][j-1][k] + a[i]);
            }
            upd(f[i][j][k], f[i][j-1][k]);
            upd(f[i][j][k], f[i-1][j][k]);
        }

    for(int i = 0; i < nc; i++) upd(ans, f[na][nb][i]);
    cout << (ans == "0" ? "0" : ans.substr(1));
}

posted @ 2022-04-01 15:17  Bellala  阅读(118)  评论(0)    收藏  举报